AVR. Учебный Курс. Использование EEPROMPrint This Post

Автор DI HALT
Опубликовано 08 Сен 2008 
Рубрики: AVR. Учебный курс
Метки: , , , ,

Иногда нужно сохранить данные так, чтобы они восстановились после перезагрузки контроллера. В этом тебе поможет 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»


  1. phil_sx 22 Окт 2008 18:39

    Спасибо! Отличная статья! Как раз сейчас нужно будет использовать EEPROM. Конечно можно прочитать даташит, что я обычно и делаю, но там все довольно разбросано, а здесь раз прочитал и в голове сложилась четкая структура.

    P.S. Еще интересно, может у тебя уже был опыт по использованию карт памяти SD/MMC? Там же вроде элементарно по SPI с ними общаться.

    DI HALT

    До SPI пока руки не доходили. А так да, в планах. С этими карточками проблема составляет не SPI доступ, а FAT :)

    phil_sx

    Впринципе туда можно любую файловую систему зарулить. Хоть Fat, хоть Ext2fs, хоть вобще BSD’шную файловую систему.
    А описаний и документации, как работать с этими системами хоть отбавляй!
    Хочу использовать SD катрочку в автомобильном логгере (запись показаний датчиков системы впрыска)


  2. Fi5t 25 Окт 2008 23:51

    Забыл написать, что у атмеловских контроллеров есть так называемая “мертвая зона” EEPROM-a. У 64й атмеги, например, это все адреса от 0×00 - 0×100. Так же была замечена тенденция (по крайней мере на атмегах), чем круче модель (ATMeag32->64->128), тем больше у нее мертвая зона EEPROM. Причем в документации, про нее нифига не написано и подбирать придется в ручную. Ах да, чем же она такая мертвая эта зона. А тем, что запись и чтение в ней происходят через раз, а то и не происходят вообще. Дрочится конечно можно, но лучше оставить эти 100 адресов на советси разработчиков и работать со стабильным ПЗУ.

    phil_sx

    Может это контроллеры бракованные были? Или может запись производилась без проверки готовности EEPROM’а?

    Fi5t

    Нет, контроллеры н бракованные и запись проводилась так как нужно, будь уверен.


  3. Natacha 23 Янв 2009 18:49

    День добрый!
    Ребята, вы все занимаетесь электроникой, помогите решить задачу, пожалуйста!!!
    Для вас это не составит труда, а человеку поможете.

    Задание:
    Разработать систему измерения частоты и скважности сигнала с ТТЛ – совместимыми уровнями и вывод результата на индикатор МТ-16S2х (частота – верхняя строка, скважность - нижняя). Диапазон измеряемых частот - 1÷1000 Гц. Абсолютный шаг измерения скважности – не менее 0,1 мс. Период накопления данных при измерении частоты – 1 секунда.
    ядро м/п системы:
    ATMEGA-8, ATTINY2313 (AVR)
    Нужны:
    расчетные параметры устройств (адреса, режимы работы и.т.д.),
    принципиальную электрическую схему системы,
    блок-схему программы, программу на языке ассемблера.

    Помогите пожалуйста, я не понимаю в этом ничего. А вам может будет интересно.
    Зарание спасибо.

    DI HALT

    Не, нам это не интересно уже. Если будут _конкретные_ вопросы, на которые можно будет дать четкий ответ, то задавай.

    Natacha

    Я бы с радостью задала вопрос, но я в этом ничего не понимаю.
    Это в институт надо принести, я студентка.
    Дали такое задание.
    Помогите пожалуйста.
    Я могу заплатить за работу.
    Просто очень надо, мне больше не к кому обратится.

    DI HALT

    Я догадался.
    Ну что я могу сказать. Вы, похоже, сильно ошиблись специальностью, мне вас искренне жаль. В таком случае ничем помочь не могу. Если кто из комментов откликнется, то может быть вам повезет.


  4. testicq 25 Фев 2009 18:07

    Блин, парюсь уже часа 3 с этим EEPROM
    В .ESEG объявил константы, так Студия не цапает их сама при эмуляции - пришлось вручную через меню указывать файл .epp. Ладно, вроде заработало.
    Но!! В Протеусе никак не могу загрузить данные EEPROM. Уж и конвертер hex2bin скачал, .epp файл конвертнул в .bin, указал в настройках ATMega8 - Initial Contens of EEPROM этот файл. А нифига - смотрю в паузе на содержимое EEPROM - везде 0xFF
    Помогите, плиз!

    testicq

    Хм… Помог сброс данных модели и последующий выбор .bin файла….
    И все-таки протеус очень веселый….

    З.Ы. Студия тоже не фонтан… Сменил в простеньком проекте мегу8 на мегу 32 - так студия перестала показывать состояние всех РВВ… Создал пустой проект, скопипастил код туда - все заработало :-) Чудеса просто…


  5. Nafigator 08 мая 2009 12:06

    А вот такой насущный вопрос:
    Собираю схему электронного одометра. Надо будет хранить пробег включая сотни метров. Ясно, что возможность записи в EEPROM закончится очень быстро. Вопрос: 100,000 записей это для всей памяти либо для одной ячейки? Т.е. могу ли я записать в оду ячейку 100000 раз потом перейти к следующей и т.д. таким образом использовать EEPROM долгое время? Если нет, то какой выход в данной ситуации можно посоветовать?
    Спасибо!

    DI HALT

    1 применить память типа FRAM от Ramtron у ней число циклов перезаписи такое, что скорей у твоей машины все молекулы сотрутся.

    2 держать данные в памяти, а на епром сбрасывать раз в 10-15 минут.

    И 100 000 это для каждой ячейки.


  6. topor123 07 июня 2009 18:50

    Почему фигня в еепром при включении питания пишется? бодлевел вроде поставил.

    DI HALT

    А боден?

    topor123

    тоже –всё согласно даташиту(тини2313).


  7. cyberr 15 июля 2009 20:17

    Я программлю в 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-ов и пользуюсь. Отлично работает :)


  8. gnomon 06 Авг 2009 1:07

    1. Ошибка. В подпрограмме чтении байта проверяется флаг готовности записи.
    2. В статье нет никакого упоминания о EECR (EPROM Control Register), он встречается только в тексте программы. Откуда будем брать флаги готовности?

    DI HALT

    1. Не ошибка. Флаг записи при чтении надо проверять чтобы быть уверены что читаем мы последние актуальные данные.

    2. Добавил в текст, спасибо.


  9. gnomon 06 Авг 2009 11:28

    По пункту 1 последнего комментария.
    А зачем проверять? Если только в контексте данного примера, учитывая, что идет сначала запись, а затем сразу же чтение и там где сейчас стоит многоточие нет кода с приличным количеством тактов? А если я просто читаю данные уже находящиеся в EEPROM, то тоже надо проверять EEWE? Получается не очень понятно из этого примера как считывать данные. imho надо было не выходить из подпрограммы записи пока не установится EEWE, т.е. убедиться что запись гарантированно завершена, тогда не понадобиться ожидать её завершения при чтении. Или я не прав? Очень неоднозначный пример получился.

    DI HALT

    В свете применения RTOS или подобной диспетчеризации проверять нужно всегда. Т.к. в разных процессах чтение и запись могут быть очень близко друг от друга по времени. А ждать окончания записи в процедуре записи еденичного байта это лишняя потеря времени. Лучше тогда отдать управление диспетчеру, а запись массива делать не на ожидании флага, а с использованием прерывания готовности — не будет потерь времени, запись пройдет фоном.

    Так что пример очень однозначный и совершенно универсальный. В конце концов, так гласит даташит. =)

    gnomon

    Не, всё равно пример не хороший или эту проверку надо в статье детально описать - зачем она нужна. Ведь в даташите конкретно расписано, что EEWE надо проверять перед чтением, т.к. возможен вариант, что запись ещё не завершена. А если я вообще ничего в EEPROM не пишу? У меня вот программа специфическая, использует подбираемые мной константы задержки расположенные в EEPROM, а сам EEPROM я дома программирую, не трогая саму программу во FLASH. Зачем мне тогда EEWE? Совершенно не зачем.
    А если я использую такую подпрограмму записи без контроля того, закончена ли запись, то возможны проблемы. Вызвал подпрограмму, записал байт и решил после этого ресетнутся и всё, кирдык - последний байт не записался. Лучше уж в подпрограмме дождаться флага готовности и тогда выходить - данные целее будут.

    DI HALT

    Всех вариантов использования не опишешь.
    Я указал самый очевидный и наиболее универсальный.

    Тем более в комментах к проге указано с какой целью делаем проверку готовности при чтении. Собственно, имхо, тут более пояснения и не требуются.
    А извраты вроде программного ресета после записи в епром это уже экзотика и если такое делаешь, то надо четко понимать последствия и и к чему это может привести. Ради такого события тупить в процедуре записи до окончания ее это нерационально с точки зрения расхода процессорного времени. Если уж хочется ресет по окончании — взвел флаг на необходимость ресета, а ресет сделается уже из прерывания по готовности епрома.


  10. rutic 13 Сен 2009 21:04

    может невтему, но у меня возникла проблема: при выполнении программы заполняется пространство ОЗУ (начинает заполняться с конца), это видно в протеусе. Я думаю это из-за записи адресов команд в стек после прерываний, в некоторых случаях я не использую reti после выполнения обработки прерывания. Можно ли как-то очистить стек? И почему такое происходит?
    микроконтроллер: MEGA16

    DI HALT

    Вот потому что не выходишь из прерывания по RETI у тебя стек и срывает. Выходи из прерывания правильно

    rutic

    а стек можно очистить?

    DI HALT

    Тебе надо восстановить стековый указатель. Такс. Адрес у нас два байта. Сделай два раза подряд POP куда нибудь в ненужный регистр. ДОлжно помочь.


  11. Virtuoso 17 Ноя 2009 18:38

    Всем Привет. Я вот тут тоже парюсь с EEPROM, в студии подгрузил файл *.eep а вот в протеусе чет никак не получается.
    Добавил в свойства контроллера в протеусе такую строку:

    {MODDATAFILE=..\MotoController\MotoController.eep}

    он стал подгружать память, но она не такая как должна быть, в студии совсем иначе. Ваще не понятно откуда протеус взял такое.

    DI HALT

    Раньше вроде в протеусе можно было выбрать не только файл прошивки но и файл епрома. Разве сейчас не так?

    Virtuoso

    протеус вставляет в память тоже самое если открыть файл *.eep нотпадом.
    я методом научного тыка пытался изменить ееп файл так как мне надо, так протеус на изменения забил и вставляет в память то что было изначально в ееп файле.

    DI HALT

    хм… возможно он ее грузит как бинарик, а еееп в хекс формате. Или наоборот, он жрет ее как хекс ,а ееп в бинаре. Я не помню формат ееп. Если что hex2bin и bin2hex утилиты тебе в помощь.

    Virtuoso

    {MODDATAFILE=..\MotoController\MotoController.eep} это строка и означает вставку файла памяти, но как я написал делает это не так как надо


  12. tolik.ms 13 Дек 2009 0:16

    Ну, спасибо! знал бы как поставить пива, не пожалел бы. Буду сёдня экспериментировать.


  13. shadrak 02 марта 2010 23:40

    Может вопрос не по теме, но все же. Разработал на базе меги 8535 тренажер, весь код залил в память программ, а нужные для работы данные в EEPROM. Т.е. получилось два
    файла [имя].hex [имя].eep, которые были успешно залиты в кристалл. Таких кристалла 4 шт., и вот в чем фокус один работает как должен, а три нет. Проверил сами кристаллы -вроде живы, проверил залитые файлы - во всех кристаллах идентичные. В чем может быть косяк?

    dima_m

    Классная статья только что ее применил в Atmega8L, все работает. После отключения питания и его восстановления сперва данные считываются из EEPROM если там 1 то моя плата входит в нужный мне режим работы если нет то и нет. Только заметил что в этой статье не описывается флаг EERIE. Флаг включения вектора прерывания после завершения процедуры записи в EEPROM.

    А еще у меня возник вопрос. В статье в примере взят начальный адрес для EEPROM 0×00, а какой может быть конечный адрес если в Atmega8 512 байт EEPROM? В даташите не нашел адресной карты ЕЕПРОМА. Для ОЗУ и для флэш памяти нашел. Но если логично предположить что у первого байта адрес 0×00 то у 512 байта он равен 0×1FF то есть 511. Так наверное.

    dima_m

    Ответ нашел таки в даташите, невнимательно смотрел. Так и есть у 512 байта конечный адрес 511. Получается адреса eeprom у atmega8 находятся в диапазоне от 0 до 511 или 0×00 до 0×1FF.


  14. SP77726262 11 мая 2010 12:48

    Добрый день уважаемый DI HALT!)
    Помогите разобраться - задыхаюсь!(
    Я новичок поэтому не судите строго…
    Мне необходимо создать динамическую таблицу констант, ну т.е. записать массив из 55 чисел и затем работать с ними (пишу на ассемблере), а результат снова записывать в этот массив, ну вот это и будет динамическая таблица.
    1. Куда записывать ? Насколько я понял нет директивы записи массива чисел в Рам, есть .db для записи байтов во флэш либо в EEPROM… Во флэш я так понимаю, не прокатит, так как надо обновлять данные, тогда EEPROM остаётся…

    2.Облазил кучу ссылок - не могу найти пример записи в EEPROM массива на азме. В вашем примере
    всё понятно, но он для записи отдельной константы. Можете помочь с куском кода для записи массива в EEPROM ?

    DI HALT

    А чем массив отличается от одного байта? Тем что там много байт. В ассемблере нет такого понятия как массив. Это все абстракция программиста. У тебя есть некоторая область в памяти. Это твой массив. У ней есть первая ячейка - начальный адрес, а адреса остальных ячеек вычисляются простым смещением относительно этой первой ячейки. Берешь и последовательно, побайтно записываешь все нужные тебе данные.

    DI HALT

    В ОЗУ можешь забабахать свой массив в виде:

    mass: .byte 55

    55 число элементов в твоем массиве. И все


  15. SP77726262 11 мая 2010 14:13

    Огромное спасибо за оперативность…
    Однако вопросов ещё много…
    Извините, я новичок:
    Каким образом мне производить запись в озу (через какую команду, неужели последовательно через РОН)?

    SP77726262

    .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 ” ???

    DI HALT

    Потому что у тебя код в дсег.

    Для кода нужно определить ксег

    DI HALT

    Да через РОН, а как ты хотел? В Z кидаешь адрес начала, а дальше командами ST Z+,Rn или им подобными гоняешь данные туда сюда. По одному байту

    SP77726262

    Сперва об ошибке компилятора: Ясно что проблема с выбором памяти для размещения. Но не понимаю куда нужно воткнуть .cseg: орг-директиву выбрал чтоб запись была после РОН и РВВ, temp - эт и есть РОН из РАМ, директива .byte тоже резервирует именно в РАМ, а ругань идёт именно на выделение места под “массив”.

    SP77726262

    Вообще офигенно!
    Сперва ошибка, что код:

    array_55: .byte 55
    STS 0×61,temp

    недопустим в .сseg - ставлю .dseg - ошибка что код лежит в дсег. %)

    SP77726262

    Всё, с этой проблемой разобрался.)

    SP77726262

    Добрый Вечер.
    Потыкался я в команды и записал таки последовательно эти 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
    ================================================================================

    DI HALT

    А я не понял, ты пытаешься инициализировать свой массив начальными значениями? Или что? Или грузишь туда результаты расчета?

    SP77726262

    Нет)
    Всё гораздо интереснее…
    Ладно карты на стол:
    1:
    Нужно сделать генерацию случайных чисел и последовательное их
    выплёвывание в порт мк.
    Для этого я использую алгоритм Митчелла Мура, суть вот в чём:
    Надо загнать в мк(использую мегу16) массив из 55 случайных чисел, соответственное 55 регистров а потом складывать
    24й и 55й - результат в 55й (если переполнение то по ходу по фиг),
    декрементируем (т.е. юзаем 23й и 54й) = результат
    в 54й, и так надо гонять этот массив (в книге термин - циклическая
    таблица).
    Когда меньший индекс (ну т.е. 24й например) станет = 0, записать в него
    55, когда старший - тоже записать(эт цикл) 0. вот…

    2: И потом всё это дело отфильтровать при помощи Цифрового фильтра,
    уравнение которого надо реализовать программно

    P.S.: Если удобно можете написать и в аську 487422360.

    DI HALT

    1. Где ты возьмешь случайные числа? Сам наплодишь или заранее таблицу сделаешь? Если заранее таблицу, то тебе проще так:

    Размещаешь ее в ПЗУ, в флешраме через .db 1,2,3 и так далее все твои 55 значений.
    Но изменять их нельзя, т.к. они в ПЗУ.

    Поэтому мы при старте через команду LPM копируем их в массив в ОЗУ, а дальше начинаем его гонять твоим методом. Получится намного компактней чем делать непосредственную загрузку из регистров.

    А можно случайные числа и так нагенерить, если есть ацп. Включить ее и начать брать самый младший бит - он почти всегда случаен. 8 самых младших битов снятых с ацп дадут тебе совершенно случайный байт. Случайный от природы - :)

    SP77726262

    Мда…
    Вопросы растут по экспоненте…
    Вот код написал, но он не работает по-человечески (массив - из таблицы случайных чисел), можете помочь?:
    ———————————————————————
    .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 случайных чисел, а потом…
    НЕ ВИЖУ компактного способа перегона этого всего в РАМ

    DI HALT

    Ну а какого хрена ты разместил массив вначале программы? Процессору все равно, он не различает данные и код и пытается твой массив случайных чисел выполнить аки команду. образуя при этом случайные команды процессора %)))

    Поставь массив ПОСЛЕ кода.

    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 - в начало цикла.

    DI HALT

    А ну еще бывают в мегах команды LPM Rn,Z+ тогда указатель зед увеличится сам


  16. SP77726262 12 мая 2010 17:46

    Привет!Спасибо за продуктивный ответ)
    Начинаю прозревать что по чём…
    Теперь хотел спросить вот что:

    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

    ————————————————————————————-

    DI HALT

    Вот ,уже лучше. Только вместо SUBI XL,(-1) лучше юзай команду st X+,R16 при сохранении, тогда у тебя Х увеличится автоматом после выполнения и SUBI XL,(-1) не потребуется вовсе.

    А не работает потому, что ты ОПЯТЬ ЗАПИХАЛ ДАННЫЕ В КОД!!!! И он опять пытается их выполнить словно команды.

    3. Что происходит когда стрелка пошагово в каждом цикле доходит до .db ?
    Происходит запись каждый раз массива во Флэш?

    НЕТ! В этот момент процессор думает, что перед ней очредная инструкция и пытается ее выполнить. ОН НЕ Различает данные и код. Данные надо писать туда куда процессор не сможет добраться самовольно. Например в самом конце программы, а перед данными делать

    RJMP Reset

    чтобы данные не выполнились.

    SP77726262

    Спасибо за комментарии - очень ценные и всё по делу.
    Код изменил - запись в РАМ прошла успешно:
    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

    SP77726262

    Теперь у меня другая задача, подскажите в какую сторону и “чем” копать:

    Сейча мне нужно брать из этого массива элемент самый последний в этой куче, то бишь 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) суммировать и перезаписывать элементы массива в РАМ.
    Читал Вашу статью по работе с памятью, всё ясно, но чтобы с декрементацией адреса регистров РАМ работать не нашёл ничего полезного…
    Что посоветуете?)

    DI HALT

    Ну почитай систему команд чтоль. Их там всего 130.

    Тебе проще наверное ввести какой нибудь базисный указатель и от него плясать, при выборе чисел. А потом просто сдвигать этот указатель и все.


  17. SP77726262 15 мая 2010 0:46

    Салют DI HALT!
    Вроде сделал я генерацию, отсимулировал тоже вроде, но не могу прошарить в
    аврСтудио, не пойму как поставить DataBreakpoint для определённого цикла (задолбался тыкать пошагово):
    * курсор после нужного Loop
    * захожу в Debug -> DataBreakpoint: ничего не меняю а лишь ставлю 18 в поле Break Execution After
    * Syntax error: Symbol expected. ????????(
    ———————————————————————————-
    И ещё, что значит символы больше и меньше подряд без пробела (типа А B ) ? Это означает A не равно B ?

    SP77726262

    блин почему-то не пишутся знаки, короче что значит:
    (А)МеньшеБольше(В) ???

Оставьте свой отзыв

Вы должны войти, чтобы оставлять комментарии.


Материалы сайта являются авторскими. Копирование и публикация материалов без активной ссылки на первоисточник запрещено.

Реклама: