В этой статье (перевод апноута AN690 Microchip [1]) описывается метод автоматического определения размера памяти EEPROM с последовательным интерфейсом I2C. Рассматриваются темы:
• Автоопределение размера памяти, подключенной к шине I2C • Стандарт I2C • Smart Serial или I2C? • Набор подпрограмм для I2C • Как распознать схему адресации • Как распознать размер • Как собрать все вместе • Отладка • Совместимость • Ссылки
[Автоопределение размера памяти, подключенной к шине I2C]
В приложениях с микроконтроллерами, где используется память Serial EEPROM, часто встает проблема согласования требований приложения и оптимизации стоимости используемых компонентов. Пользователю часто нужно использовать разные размеры памяти для разных версий приложения, однако ценовые ограничения требуют использовать каждый раз минимальный размер памяти. Примером типового приложения может быть базовая станция (приемник) дистанционно управляемой системы открытия двери гаража. Версии, которые могут хранить в памяти 4, 20, 200 или 1000 пользователей, могли бы с одним и тем же исходным кодом, подходящим к различным используемым микросхемам памяти.
Microchip поставляет широкий диапазон емкости памяти с интерфейсом I2C (от 16 байт в 24C00 до 32k байт 24C256). У микроконтроллера должен быть способ определить, какая память используется на шине I2C, чтобы можно было правильно её адресовать.
Для решения этой проблемы есть 2 возможных метода - один состоит в предоставлении какой-либо конфигурационной информации для контроллера, например с помощью DIP-переключателей или перемычек, и другой способ подразумевает автоматическое определение конфигурации памяти. В этом апноуте показано, как просто и безопасно реализовать автоматическое детектирование подключенной памяти.
Описанные программные техники демонстрируются на обычном микроконтроллере PIC16C62A среднего ценового диапазона, и это может быть немедленно протестировано на демо-плате PICDEM2. Весь код может быть адаптирован для других микроконтроллеров семейства PICmicro (с ядрами 12, 14, 16 и/или другими конфигурациями выводов) путем незначительных изменений в исходном коде.
[Стандарт I2C]
Протокол I2C использует двунаправленную шину, работающую по принципу главного устройства и подключенных к нему подчиненных устройств (схема master/slave). Главное устройство (master) это обычно микроконтроллер, который управляет шиной, генерирует такты (SCL) и формирует сигналы START и STOP на шине. Serial EEPROM ведет себя как подчиненное (slave) устройство, и оно работает как передатчик данных во время операций чтения. Во время операций записи EEPROM работает как приемник, формируя сигналы положительного и отрицательного подтверждения (ACK и NACK) для устройства master. Сигналы START и STOP используются для управления потоком данных по шине. Нормальная операция начинается с сигнала START и заканчивается сигналом STOP. За START начинается команда чтения или записи, содержащая адрес устройства, эту команду формирует master. Этот первый байт называется байтом управления, он идентифицирует подчиненное устройство и определяет следующую операцию - чтение это будет или запись. Типовой байт управления для Serial EEPROM показан на рис. 1. Таким образом, байт управления начинается с сигнала START, за которым идет 4-битный идентификатор типа устройства (для устройства типа EEPROM это будут биты 1010), затем 3 бита, выбирающие конкретную микросхему на шине I2C (A2, A1, A0), бит чтения/записи (R/W), за которым должно идти подтверждение от slave-устройства (ACK).
Рис. 1. Структура байта управления.
[Smart Serial или I2C?]
Последовательная шина I2C имеет много преимуществ перед другими последовательными шинами для встраиваемых устройств. Шина I2C с триггерами Шмитта на входах, срабатывающих по уровню, дает лучшую помехозащищенность по сравнению с технологиями, которые основаны на перепадах уровня. Для обмена с микросхемами памяти не требуются коды операций и используют интуитивно-понятную схему операций.
Однако стандартный протокол ограничивает адресацию памяти максимумом 16K байт при использовании 8-битного адреса и трех выводов A0, A1 и A2 (8 x 2 килобайта). Здесь заключается некая дилемма. С появлением более современных персональных устройств связи, таких как сотовые телефоны, наладонники и другие переносные устройства, 16K байт будет недостаточно.
Поэтому в связи с требованиями индустрии к повышению размеров памяти I2C во встраиваемых приложениях появилась концепция Smart Serial, удовлетворяющая требованиям уменьшения износа памяти, повышения безопасности данных. Также с этой концепцией была добавлена функциональность поддержки пониженного потребления энергии.
Компанией Microchip была разработана схема адресации для I2C Serial EEPROM, основанная на стандартном протоколе I2C и адресе устройства, но был введен дополнительный байт адреса, чтобы позволить разработчику применить до 256K бит на устройство и добавить от 1 до 8 устройств на одну системную шину I2C. Это позволило будущее расширение памяти, и дало продвинутые возможности в маленьких, эффективных по стоимости разработках.
Для первого байта, или управляющего байта, Smart Serial придерживаются стандартного протокола I2C (см. рис. 2). Следующие 2 байта (вместо одного) определяют адрес запрашиваемой области памяти.
Рис. 2. Запись байта.
[Другой набор подпрограмм для шины I2C]
Компанией Microchip Technology было опубликовано множество апноутов с интерфейсом к шине I2C: AN515, AN537, AN558, AN567, AN608, AN554, AN578 и AN535. Ниже будут будут использованы техники из этих даташитов, и на основе их кода будут создан компактный, мощный набор подпрограмм. Сначала будет модифицирован базовый набор подпрограмм [3, 4, 6, 8, 10], чтобы они поддерживали адресацию Standard I2C и Smart Serial, выбирали runtime схему адресации посредством флага SMART.
Листинг 1 (i2c.inc) показывает новый набор подпрограмм. Как обычно, здесь присутствует 2 слоя функций:
• Нижний слой (состоящий из подпрограмм: BSTOP, BSTART, RXI2C, TXI2C, BITIN, BITOUT, ERR), который реализует отправку и определение одиночных бит и байтов на шине, и на этом слое нет нового кода.
• Верхний слой (состоящий из подпрограмм: RDbyte, WRbyte и SETI2C), который обрабатывает команды и заботится о схемах адресации. В этой документации в основном обсуждается именно этот слой.
Нововведение здесь следующее: в функцию SETI2C перенесен весь код, который работает с деталями схемы адресации. Эта функция получает флаг SMART на входе, и предоставляет адресацию Standard или Smart в соответствии с его значением. Функции RDbyte и WRbyte полагаются на SETI2C для генерации команды и адреса, чтобы добиться совместимости со Standard и Smart Serial.
[Определение схемы адресации]
Следующий небольшой шаг к автоматическому определению размера памяти - нам нужно найти метод, чтобы автоматически отличить Smart Serial и Standard Serial EEPROM. Предлагается простой и компактный алгоритм, состоящий из 4 шагов:
1. Перевод I2C подпрограмм в режим Smart Serial (установкой флага SMART).
2. Выдается команда write в ячейку 0000, с записью 1.
Если память стандартная I2C, то эта команда интерпретируется как команда последовательной записи (sequential write) двух байт, в результате запишется байт 00 в ячейку 0000 и байт 01 в ячейку 0001. Если же память Smart Serial, то мы получим корректную интерпретацию адреса, и в ячейку 0000 будет записан байт 01.
3. Подпрограммы I2C переводятся в стандартный режим (Standard I2C Mode, путем очистки флага SMART).
4. Выдается команда read ячейки 0000.
Если память действительно Standard I2C, то эта команда read даст нам содержимое ячейки 0000, которая была установлена в 0. Если же память типа Smart Serial, то получится команда чтения с частичной (неполной) адресации. То, что произойдет, не относится непосредственно к работе шины I2C, давайте проанализируем эти два возможных случая.
a) Частичная адресация только установит старшие значащие биты внутреннего регистра адреса, и оставит не измененными младшие 8 бит. Это означает, что будет прочитана ячейка 0000. b) Частичная адресация не модифицирует весь регистр адреса. Это означает, что адрес остается равным последнему установленному значению (последней операцией Smart Write), и чтение даст содержимое ячейки 0000.
Если в обоих случаях процесс завершится чтением 1, то это укажет нам на память Smart Serial. Если будет прочитан 0, то это последовательная память Standard I2C.
Листинг 2 (i2cauto.asm) реализует этот простой алгоритм на 10 строках ассемблера.
Замечание: очевидно, что описанная процедура повредит содержимое ячеек 0000 и 0001, и не существует способа сохранить и восстановить их (пока не будет определена схема адресации!).
[Определение размера памяти]
Последний шаг к автоматическому определению размера памяти - разработка алгоритма, который определит размер памяти по имеющейся схеме адресации. Предположим, что уже известен тип памяти - Standard или Smart, и необходимо определить её размер.
Детектирующий алгоритм будет базироваться на простом предположении: если память имеет размер N, то попытка адресации вне диапазона 0..N-1 приведет к попаданию обратно в этот диапазон. Поскольку самые старшие (дополнительные) биты адреса будут просто игнорироваться, для устройства они не будут иметь значения. Это можно просто проверить по даташиту каждой микросхемы памяти.
Можно разработать простую тест-функцию, которая скажет нам, имеет ли память размер N, или у неё размер меньше. На псевдокоде эта функция будет выглядеть примерно так:
boolTestIfSizeIs (u32 N)
{
// Адреса памяти находятся в диапазоне 0..N-1?
u8 TEMP;
TEMP = Read(0000);
if (Read(N) == TEMP)
{
Write(0000, TEMP+1);
if (Read(N) == TEMP+1)
{
Write(0,TEMP-1);
return TRUE;
}
}
// Иначе:return(FALSE)
}
С помощью такой функции мы можем организовать цикл проверки размеров памяти. В случае памяти Standard I2C цикл может проверять от N=128 до N=2048 (микросхемы от 24C01 до 24C16) удваивая N на каждой итерации:
u8 StandardI2CMemDetect (void)
{
// Функция вернет номер модели микросхемы памяти 1..16.
u16 N =128;
u8 MODEL =1;
do
{
if (TestIfSizeIs(N))
break;
else
{
N=N*2;
MODEL=MODEL*2;
}
}
while(N <=2048);
return MODEL;
}
Подобным образом функция может проанализировать память Smart Serial в диапазоне от N=4096 до N=32768.
Обратите внимание, что в этом простом алгоритме не резервируются ячейки памяти. Изменяемая ячейка в процессе тестирования всегда должна быть сохранена и восстановлена.
Все готово для реализации алгоритма определения размера микросхемы. Сначала мы определим схему адресации, после чего войдем в цикл, измеряющий реальный размер памяти. В зависимости от схемы адресации цикл будет проверять разные диапазоны размеров памяти, соответствующие разным размерам моделей микросхем памяти, доступных на рынке.
Листинг 2 (i2cauto.asm) реализуют на ассемблере оба алгоритма (определение схемы адресации и размера).
Код может быть протестирован на PIC16C62A (плата PICDEM2) или любой другой целевой системе (после модификации используемых ножек выводов листинга 2 i2cauto.asm). После установки памяти I2C в сокет DIL платы PICDEM2, подайте на неё питание или нажмите кнопку сброса, и светодиоды покажут в двоичном представлении значение TYPE (см. таблицу 1).
Таблица 1. Значение TYPE для разных микросхем памяти.
Standard I2C
Smart Serial
TYPE
Размер
Модель
TYPE
Размер
Модель
01
128
24C01 24C21 24C41
32
4096
24C32
02
256
24C02 24C62
64
8192
24C64 24C65
04
512
24C04
128
16384
24C128
08
1024
24C08
0
32768
24C256
16
2048
24C16 24C164
Вы можете экспериментировать и изменить предлагаемый код (см. ниже врезку "Приложение A: код ассемблера для PIC16C62A"), чтобы он соответствовал Вашим определенным требованиям. Однако старайтесь учитывать допустимое количество перезаписей (ограниченный ресурс) микросхем EEPROM, и используйте программную утилиту "Endurance" от Microchip Technology, когда разрабатываете приложения с особыми требованиями к надежности [11, 12].
[Совместимость]
Почти весь представленный код строго следует существующим стандартам I2C и Smart Serial, поэтому он должен быть совместим с любым устройством Serial EEPROM от любого производителя, который придерживается этих стандартов. Однако код проверялся только с микросхемами Microchip Serial EEPROM, и пользователь должен самостоятельно обеспечить совместимость кода с микросхемами памяти от других производителей.
Кроме того, возможны некоторые проблемы совместимости на шаге детектирования схемы адресации. Фактически поведение последовательной памяти в случае частичной адресации (что происходит на шаге 4 в случае Smart Serial) не является частью спецификации. Несмотря на то, что с текущей реализации протокола Smart Serial предлагаемое решение работает нормально (с микросхемами Microchip вплоть до 24C256), не гарантируется, что это также хорошо будет работать и в будущем.
FLAGSINDHI; адресINDLODATO; буфер данных для функций чтения и записиERCODE; код ошибки (см. таблицу ниже)EEBUF; буфер чтения/записиSLAVEbuf; SLAVE-адрсе (+ addrHi на 24LC16)COUNTAUX
RDbytebcfFLAG_EE; сброс флага ошибкиcallSETI2C; установка указателя адреса
; вход в последовательное чтение (sequential reading)
RDnextcallBSTART; STARTmovfSLAVEbuf,W; используется SLAVE addr(+IndHi при 24LC16)movwfEEBUFbsfEEBUF,0; команда чтенияcallTXI2C; вывод SLAVE + address + команда readcallRXI2C; чтение в DATO и ACKnowledgemovfEEBUF,WmovwfDATObsfSTATUS,C; ACK = 1 (NOT ACK)callBITOUT; для остановки ввода (STOP)gotoBSTOP; генерация STOP-бита
WRbytebcfFLAG_EE; сброс флага ошибкиcallSETI2C; установка указателя адресаmovfDATO,W; перемещение DATOmovwfEEBUF; в буферcallTXI2C; вывод DATO и детектирование ACKnowledgecallBSTOP; генерация STOP-бита; цикл ожидания завершения записиmovlw.80; 80 = таймаут 20 мсmovwfAUX
WRpollCLRWDT; сброс сторожевого таймераbcfFLAG_EEcallBSTART; стартmovlwSLAVEmovwfEEBUFcallTXI2C; и команда записиbtfssFLAG_EE; если не ACK -> код ошибки 3 -> BUSYgotoWRpollE
TXI2Cmovlw.8; Set counter for eight bitsmovwfCOUNT
TXlprlfEEBUF,F; бит данных находится в CARRYcallBITOUT; отправка битаdecfszCOUNT,F; отправлено 8 бит?gotoTXlp; нет...callBITIN; чтение бита ACKmovlwERR_NACKbtfscSTATUS,C; проверка подтвержденияcallERR; нет подтверждения от устройстваreturn
Bit1bsfSTATUS,RP0; выбор RAM bank 1bsfSDA; вход SDA (pull up -> 1)bcfSTATUS,RP0; обратно в RAM bank 0movlwERR_LOCKbtfssSDA; Проверка на ошибкуcallERR; SDA заблокирован микросхемой памятиgotoClk1
Bit0bsfSTATUS,RP0; выбор RAM bank 1bcfSDA; выход SDAbcfSTATUS,RP0; обратно в RAM bank 0bcfSDA; сброс в 0nop; задержка
Clk1bsfSCL; перевод SCL в лог. 1nopnopnop; задержка минимум 4 мксnopnopbcfSCL; перевод SCL в лог. 0return
BITINbsfSTATUS,RP0; выбор RAM bank 1bsfSDA; установка SDA для вводаbcfSTATUS,RP0; обратно в RAM bank 0bsfSCL; SCL = 1nopnopnopnop; предоставляется минимальное время установкиCLRCbtfscSDA; чтение SDA в CARRYbsfSTATUS,CbcfSCL; возврат с SCL = 0return
BSTARTbsfSTATUS,RP0; выбор RAM bank 1bsfSDA; вход SDA (pull up -> 1)bcfSTATUS,RP0; обратно в RAM bank 0bsfSCL; SCL = 1nopnopnopnop; 5 мкс перед спадом SDAbsfSTATUS,RP0; выбор RAM bank 1bcfSDA; выход SDAbcfSTATUS,RP0; обратно в RAM bank 0bcfSDA; SDA = 0nopnopnopnop; 4 мкс перед спадом SCLbcfSCL; начало последовательности тактовreturn
BSTOPbsfSTATUS,RP0; выбор RAM bank 1bcfSDA; выход SDAbcfSTATUS,RP0; обратно в RAM bank 0bcfSDA; SDA = 0bsfSCL; SCL = 1nopnopnopnop; 4 мкс перед нарастанием SDAbsfSTATUS,RP0; выбор RAM bank 1bsfSDA; вход SDA (pull-up -> 1) пока SCL = 1bcfSTATUS,RP0; обратно в RAM bank 0movlwERR_STOP; получение кода ошибкиbtfssSDA; лог. 1?callERR; ошибка, SDA заблокирован перед STOPbcfSCL; SCL = 0return
MemDetectclrfINDHI; адрес 0000hclrfINDLObsfSMART; write(smart, 0000, 1)movlw1movwfDATOcallWRbytebcfSMARTcallRDbyte; read(standard, 0000)movfDATO,WbtfscSTATUS,ZgotoStandardD
SmartDbsfSMART; это Smart SerialmovlwHIGH(.4096)
movwfSIZEHI; размер = 4096 байтclrfSIZELOmovlw.32movwfTYPE; начинаем с TYPE = 24C32gotoTestD
StandardDbcfSMART; это Standard Serialmovlw.128movwfSIZELO; размер = 128 byteclrfSIZEHImovlw01movwfTYPE; начинаем с TYPE = 24C01
TestDcallRDbyte; TEMP=read(0)movfDATO,WmovwfTEMP
LoopDetmovfSIZELO,W; DATO=read(SMART, size)movwfINDLOmovfSIZEHI,WmovwfINDHIcallRDbytemovfDATO,WxorwfTEMP,W; сравнение TEMP с DATObtfssSTATUS,ZgotoLoopDNincfTEMP,W; если равно, то TEMP=TEMP+1movwfTEMPmovwfDATOclrfINDHIclrfINDLOcallWRbyte; write(SMART, 0000, TEMP)movfSIZELO,W; if (read(SMART, size) == TEMP)movwfINDLOmovfSIZEHI,WmovwfINDHIcallRDbytemovfDATO,w; если все еще одинаковое значение, то это значит,xorwfTEMP,W; что мы достигли реального размера памятиbtfscSTATUS,ZgotoDetEx
StartbsfSTATUS,RP0; выбор RAM bank 1movlwMASKA; установка регистров trismovwfPORTA; PORTAmovlwMASKB;movwfPORTB; PORTBmovlwMASKC;movwfPORTC; PORTCmovlwb'00000111'; разрешение pullup, прескалер TMR0 1:256movwfOPTION_REGbcfSTATUS,RP0clrfFLAGS; сброс всех флагов
MaincallMemDetect; определение размера памятиmovfTYPE,W; если используется плата PICDEM2, тоmovwfPORTB; TYPE отправляется на светодиоды LED
MainLoopgotoMainLoop; зацикливание до сбросаEND
[Ссылки]
1. AN690 I2C Memory Autodetect site:microchip.com (00690a.pdf). 2. Best way to detect EEPROM size site:avrfreaks.net. 3. AN515 Communicating with I2C™ Bus Using the PIC16C5X, Bruce Negley. 4. AN535 Logic Powered Serial EEPROMs, R. J. Fisher and Bruce Negley. 5. AN558 Using the 24xx65 and the 24xx32 with Stand-alone PIC16C54 Code, Dick Fisher and Bruce Negley. 6. AN567 Interfacing the 24LCxxB Serial EEPROMs to the PIC16C54, Bruce Negley. 7. AN608 Converting to 24LCXXB and 93LCxx Serial EEPROMs, Nathan John. 8. AN536 Basic Serial EEPROM Operation, Steve Drehobl. 9. AN554 Software Implementation of I2C™ Bus Master, Amar Palacherla. 10. AN559 Optimizing Serial Bus Operations with Proper Write Cycle Times, Lenny French. 11. AN537 Serial EEPROM Endurance, Steve Drehobl. 12. AN602 How to get 10 Million Cycles Out of Your Microchip Serial EEPROM, David Wilkie.