Запись данных во внешнюю flash-память микроконтроллера

June 27, 2010 by admin Комментировать »

Предположим, мы хотим, чтобы данные с нашего измерителя температуры и давления каждое три часа (это т. и. метеорологический интервал) записыва­лись в энергонезависимую память. Для решения этой задачи схему измерителя (см. рис. 20.4) придется слегка доработать — так, как показано на рис. 21.8.

clip_image002

Рис. 21.8. Присоединение внешней EEPROM к измерителю температуры и давления

Здесь используется энергонезависимая память типа АТ24С256. Она имеет структуру EEPROM (то есть с индивидуальной адресацией каждого байта), но чтобы отличить ее от встроенной EEPROM, в дальнейшем мы будем внешнюю память называть flash (хотя это и не совсем корректно). Последнее число в обозначении означает объем памяти в килобитах, в данном случае это 256 кбит или 32 768 байтовых ячеек (32 кбайт). Объем памяти в 32 кбайт кажется смешным в сравнении с современными разновидностями flash, кото­рые уже достигают объемов 32 Гбайт, но для наших целей, как вы увидите, этого будет более чем достаточно. Кроме того, память суилественно больших объемов с интерфейсом I2C не выпускают — слишком уж он медленный.

Заметки на полях

Чтобы прочесть 32 Кбайта со скоростями I2C, потребуется примерно 0,5 мс на каждый байт, то есть около 16 секунд. Потому максимальный объем памяти с таким интерфейсом фирмы Atmel, к примеру, составляет 1 Мбит (АТ24С1024). Память с бдльшими объемами, во-первых, представляет собой действительно flash-память (с блочным доступом), а во-вторых, выпускается с интерфейсами побыстрее.

Кстати, найти в продаже микросхемы EEPROM с последовательным досту­пом (в том числе и использованную в нашей схеме АТ24С256) в корпусе DIP довольно сложно. АТ24С256 (как и упоминающаяся далее АТ24С512 и дру­гие) чаще встречается в миниатюрном корпусе SOIC с 8-ю выводами. Так как присоединять в простейшем случае приходится всего 4 вывода (2 питания и 2 сигнальных), то даже при ручной разводке это не доставляет больших сложностей.

Адресация в микросхеме АТ24С256 двухбайтовая, потому под адрес задейст-вуется два регистра (AddrL и AddrH). Мы выбираем г24 и г25 (см. текст про­цедур в пршожении 4) — почему именно эти, вы увидите далее. Записывае­мые данные будут храниться в регистре data. Эти регистры являются входными переменными как для процедуры записи, так и чтения.

Теперь давайте определимся, что именно мы будем записывать и на сколько нам хватит этой памяти. Базовый кадр данных у нас будет состоять из четы­рех байт значений давления и температуры. Мы можем, конечно, писать и в распакованном BCD-виде, взяв подготовленные для индикации значения, но зачем загромождать память (кадр тогда состоял бы из шести байт, а не четы­рех), если коэффициенты пересчета мы знаем (они всегда могут быть прочи­таны из EEPROM), и уж в компьютере-то пересчитать всегда сможем. Тогда в наши 32 кбайт мы сможем вместить 8192 измерения (на самом деле чуть меньше, как мы увидим, но это несущественно), то есть при трехчасовом цикле (8 измерений в сутки) памяти нам хватит на 1024 суток, или почти на 3 года записей!

Если вам нужно записывать данные чаще, можно увеличить память еще в несколько раз. Кристалл АТ24С512 вдвое большего объема можно поставить сюда без изменений в схеме и программе (кроме задания максимального ад­реса). Кроме того, схемотехника серии АТ24 предполагает возможность ус­тановки параллельно четырех или восьми таких микросхем (с заданием ин­дивидуального I2C-адреса для каждой), так что при желании объем можно увеличить еще в четыре—восемь раз. Причем использовать, например, две АТ24С512 целесообразнее, чем одну АТС 1024, так как для последней адре­сация усложняется (адрес для объема 128 Кбайт содержит 17 бит и выходит за рамки 2-байтового).

Подробности

Микросхемы серии АТ24 имеют два (или три для микросхем с буквой В в конце обозначения) специальных вывода АО и А1 (выводы 1 и 2 для 8-выводных кор­пусов), которые задают индивидуальный I2C-адрес. Если эти выводы ни к чему не подсоединять (как в нашей схеме), то считается, что они подсоединены к логическому нулю. Тогда I2C-адрес микросхемы будет 1010000 в двоичной форме (или $А0/$А1 в шестнадцатеричной на запись/чтение), см. листинг про­цедур I2C в приложении 4. Если на указанные выводы адреса подавать сигна­лы, то адрес такой микросхемы будет определяться как 10100<А1><А0>. Та­ким образом, выбором конкретной микросхемы можно управлять, подавая на эти выводы сигнал по дополнительным линиям, которые фактически будут 17-м и 18-м битами адреса. Другой способ — запаять «навЬегда» различные адреса для каждой микросхемы на плате, и для каящой из них формировать свой байт адреса.

Для того чтобы записывать исходные значения температуры и давления, нам их придется где-то хранить отдельно, отведя для этого специальные ячейки в SRAM. Сама запись производится очень просто: с каждым байтом мы увели­чиваем на единицу содержимое счетчика адресов AddrH:AddrL (командой adiw — именно для ЭТОГО и выбирались регистры г24 и г25, чтобы ее можно было использовать), «забиваем» нужный байт в регистр data и вызываем процедуру WriteFlash.

Но тут встают две проблемы. Во-первых, надо решить, что делать, когда па­мять закончится. Тогда нужно либо обнулять счетчик адресов и начинать за­пись заново, поверх младших адресов, либо, что гораздо красивее, остано­вить запись, пока содержимое ее не будет прочитано и адрес принудительно не будет обнулен. То есть потребуется какой-то флаг, сигнализирующий о том, что настал конец памяти. Причем отвести для этого флага, например, бит в регистре Flag, будет недостаточно: а что будет при сбое питания? Нам придется хранить где-то во встроенной EEPROM и этот флаг и, главное, те­кущий адрес памяти, иначе данные будут пропадать после каждого отключе­ния питания.

Во-вторых, нам надо как-то отсчитывать время, когда производить запись — для того чтобы метеоданные были полноценными, их нужно привязать ко времени. И тут мы неизбежно приходим к тому, чтобы объединить часы с нашим измерителем. Этим мы займемся чуть далее, потому что, как мы гово­рили, использовать сам контроллер в качестве часов нецелесообразно, а под­ключение часов сложнее, чем памяти.

А пока, чтобы отработать процедуры обмена по I2C, договоримся, что запись в память у нас будет производиться по прерыванию Timerl, который все равно в измерителе больше ничем не занят. При 4 МГц тактовой частоты и максимально возможном коэффициенте ее деления 1024, можно заставить Timer 1 срабатывать каждые, например, 15 секунд, для чего в регистр срав­нения придется записать число 58 594 (проверьте!). С такой частотой память, конечно, заполнится оч^нь быстро (32 кбайт— менее чем за 1,5 суток), но это, наоборот, удобно, если стоит задача проверить все наши процедуры.

Итак, дописываем определения в соответствующую секцию программы из­мерителя, там, где адреса SRAM:

;Нех-данные – сырые, без пересчета

.equ Thex = ОхОА ;OA,OB – старший и младший байты температуры .equ Phex = ОхОС ;OC,OD – старший и младший байты давления .equ FEnRAM = $0Е ;флаг, если равен $FF, то писать во flash

Отдельно запишем адреса в EEPROM (первые восемь у нас заняты коэффи­циентами):

.equ FEnEE = 0x10 ;флаг если равен $FF, то писать во flash .equ EaddrL =0x11 ;младший байт текущего адреса .equ EaddrH =0x12 /старший байт текущего адреса

Обратите внимание, что запись во flash разрешена, если байт еепЕЕ равен $FF, то есть в самом начале, когда EEPROM еще пуста, запись по умолчанию разрешается. В процедуре обработки данных дописьшаем процедуры сохране­ния «сырых» значений температуры и давления по указанным адресам. Они у нас содержатся в регистрах АгедН:Агедь (см. главу 20) В начале обработки данных по температуре после имеющегося оператора rjmp prs дописываем:

Idi ZL,Thex /запоминаем температуру St Z+,AregH St Z,AregL

A там, где начинается расчет давления, после оператора rjmp contPT записы­ваем:

Idi ZL,Phex /запоминаем давление St Z+,AregH St Z,AregL

Теперь инициализируем таймер. В загрузочную секцию вместо строк ини­циализации Timer О (idi temp, (i«ToiEO) И out TIMSK, temp) добавляем та-кой код:

/++++++++Set Timer 1 Idi temp,high(58594) out OCRlAH,temp Idi temp,low(58594) out OCRlAL,temp Idi temp,0b01000000

out TCCRIA,temp /переключающий режим для вьшода PD5-0C1A Idi temp,0b00001101

out TCCRIB,temp /1/1024 очистить после совпадения

Idi temp, (1«TOIEO) I (1«0CIE1A) /разрешение прерьшания

/по совпадению для Timer 1 и переполнению Timer О out TIMSK,temp

К выводу 0С1А (вывод 19 для ATmega8535) можно присоединить светодиод, ко­торый будет попеременно гореть и гаснуть с периодом 30 с, показывая, что за­пись работает (если помните, он и был задействован в таком качестве в часах).

Далее в секции начальной загрузки инициализируем регистры адреса. Полу­чится довольно сложная процедура, которая должна проверять значения ад­реса в EEPROM, и если он записан (то есть память не пуста и там не записа­ны все единицы), то еще и сравнивать его с последним возможным адресом (32767 или 7FFFh):

/ ===*=====инициализация адреса флеш с1г ZH /старший EEPROM Idi ZL,EaddrL /младший EEPROM

rcall ReadEEP mov AddrL,temp ldi ZL,EaddrH rcall ReadEEP

mov AddrH,temp /теперь в AddrH:AddrL адрес из EEPROM ldi temp,OxFF /если все FF, то память была пуста ср AddrL,temp ldi temp,OxFF срс AddrH, temp brne cont_l

clr AddrH /если пуста, то присваиваем адрес = О clr AddrL

clr ZH /старший EEPROM

ldi ZL,EaddrL /младший EEPROM

mov temp, AddrL

rcall WriteEEP /и записываем его опять в EEPROM inc ZL

mov temp,AddrH rcall WriteEEP cont_l: /теперь проверяем на последний адрес $7FFF ldi temp,OxFF ср AddrL, temp ldi temp,0x7F cpc AddrH, temp brne cont_2

sbr Flag,4 /4 бит регистра Flag = конец памяти cont_2: /загрузка байта разрешения записи flash clr ZH /старший EEPROM ldi ZL,FEnEE rcall ReadEEP ldi ZH,1 /старший RAM ldi ZL,FEnRAM /младший RAM St Z,temp /сохраняем значение флага

Отдельный бит «конец памяти» в регистре Flag (бит 2, который устанавлива­ется командой sbr Flag, 4) нам понадобится позднее для того, чтобы можно было временно запретить запись во flash внешней командой, не сбрасывая значения адреса и независимо от того, достигнут конец памяти или нет.

Теперь в секции прерываний заменим reti на rjmp timicompa в строке для прерывания Timer 1 Compare А (шестое сверху, не считая RESET), и напи­шем его обработчик:

Т1М1_С0МРА: ;15 секунд

/проверять разрешение записи во flash

ldi ZH,1 /старший RAM

ldi ZL,FEnRAM ^

Id teirp,Z

cpi temp,$FF

breq flag__WF

reti /если запрещено, то выходим из прерьшания flag_WF:

ldi ZL,Thex /адрес значения в SRAM Id DATA,Z+ / старший Т rcall WriteFlash /пишем во flash adiw AddrL, 1 Id DATA, Z+ /младший T rcall WriteFlash adiw AddrL, 1 Id DATA,Z+ /старший P rcall WriteFlash adiw AddrL,1 Id DATA, Z+ /младший P rcall WriteFlash /проверяем адрес на 7FFF ldi temp,OxFF ср AddrL, temp ldi temp,0x7F cpc AddrH, temp

breq clr_FE если равен, на clr__FE

adiw AddrL,1 /иначе сохраняем следующий адрес:

clr ZH

ldi ZL,EaddrL /в EEPROM mov temp,AddrL

rcall WriteEEP inc ZL

mov temp,AddrH

rcall WriteEEP reti ;выход из прерывания clr_FE /если конец памяти:

clr temp

Idi ZH,1 /старший RAM Idi ZL,FEnRAM

St Z,temp /сбрасываем разрешение записи clr ZH /старший EEPR Idi ZL,FEnEE rcall WriteEEP

sbr Flag,4 /бит «конец памяти» clr temp

out TCCRIA,temp /отмена переключающего режима для вывода PD5-0C1A reti /выход из прерывания

Как видим, здесь каждые 15 секунд идет запись в EEPROM текущего адреса (того, по которому должна производиться следующая запись), то есть если в какой-то момент питание пропадет, то при следующей загрузке запись все равно начнется с текущего адреса. При достижении конца памяти отключит­ся переключающий режим для вывода ociA, и светодиод перестанет мигать, сигнализируя о конце памяти.

Заметки на полях

Обратите внимание, что в процедурах I2C используются псевдонимы — data и cikA есть те же регистры, что и tempi и temp2 в основной программе. Я предос­терегал вас от такой практики, но в данном случав ничего страшного не про­изойдет, так как использование этих регистров разнесено во времени — обра­щение к I2C никогда не сможет произойти во время расчетов, где используются tempi и temp2. В дальнейшем мы еще где-нибудь эти регистры используем.

Оставить комментарий

микросхемы мощности Устройство импульсов питания пример приемника провода витков генератора выходе напряжение напряжения нагрузки радоэлектроника работы сигнал сигнала сигналов управления сопротивление усилитель усилителя усиления устройства схема теория транзистора транзисторов частоты