Внешняя память EEPROM серии 24cXX и микроконтроллер AVR

В некоторых конструкция, более интересных и сложных, необходимо сохранять переменные на время отключения питания, или вести журнал изменения переменной (например если устройство предназначено для отслеживания изменения температуры на протяжении суток). Для этих целей необходимо иметь хранилище данных не зависимое от питания устройства, то есть энергонезависимое. Например для хранения нескольких переменных, таких как например последнее значение температуры перед отключением питания, или пароль кодового замка необходимо всего несколько байт памяти. Для таких задач вполне хватает штатной, встроенной в микроконтроллер энергонезависимой памяти. А что делать если необходимо сохранить несколько килобайт данных.

Или записать небольшой файл в память устройства, или просто устройству не хватает например памяти для хранения текста, выводимого потом на экран. Как пример анимация в виде серии картинок (кадров) для вывода на дисплей NOKIA 3310, картинки занимают очень много памяти, они просто не влезут в память микроконтроллера.

Решить задачу поможет микросхема внешней памяти EEPROM. EEPROM - (Electrically Erasable Programmable Read-Only Memory) что значит Программируемая Память с Электрическим Стиранием.

То есть такие микросхемы предназначены для хранения данных без внешних источников питания. Им не страшно отключение питания. Их легко можно стереть, выполнив определенную команду. Данные микросхемы работают по протоколу I2C что подразумевает высокую скорость работы.

Организация пами Микросхемы EEPROM представляют из себя таблицу с двумя столбиками, 1-й - адрес, 2-й - значение.

Адрес Данные
0000 L
0001 1
0002 3
0003 f
... ...
n x


Адрес ограничивается только номиналом микросхемы EEPROM. Номиналы микросхем бывают:
24c02, 24c08, 24c16, 24c32, 24c64, 24c128, 24c256, 24c512 изредка но можно найти.
Все микросхемы серии абсолютный аналог друг-друга.

Значение поля "Данные" ограничивается пределами типа данных int, то есть от -32767 до 32767. Данные лучше всего записывать в шестнадцатиричной системе, то есть:
в десятичной системе число "35" будет соответствовать значению "0x23" в шестнадцатиричной.
От себя:
Для записи например значения температуры лучше всего использовать несколько ячеек памяти.
Так например температуру +37,5 лучше всего разбить на три ячейки:
1. знак температуры (+/-)
2. температура до запятой (37)
3. температура после запятой (5)
Запятую в таком случаи необходимо будет устанавливать программно для вывода значения температуры напрмиер на дисплей, после первых трех символов значения.

Микросхемы EEPROM выпускаются как в корпусах типа DIP так и корпусах для поверхностного монтажа SOIC. Если к устройству нет определенных жестких требований по части корпуса, то можно использовать и DIP корпус, разницы нет.

Обычно микросхемы серии 24cХХ отличаются лишь объемом внутренней памяти.
Рассмотрим пример программы для работы с одной из этих микросхем. Программа ориентирована на работу микроконтроллера ATmega8 и микросхемы внешней EEPROM 24c64, схема подключения 24c64 к микроконтроллеру ATmega8 показана на рис. 1
схема сключения 24c128
Рис. 1

Содержание файла 24c64.c, файл содержит набор подпрограмм для работы с внешней EEPROM памятью по шине I2C(по сути в данном коде не чистый I2C а программный посредством интерфейса TWI).

  1. #include "24c64.h"
  2.  
  3. void EEOpen()
  4. { //Конфигурация TWI модуля
  5. TWBR = 5;
  6. TWSR &= (~((1<<TWPS1)|(1<<TWPS0)));
  7. }
  8.  
  9. uint8_t EEWriteByte(uint16_t address,uint8_t data)
  10. { do {
  11. //Put Start Condition on TWI Bus
  12. TWCR=(1<<TWINT)|(1<<TWSTA)|(1<<TWEN);
  13.  
  14. //Poll Till Done
  15. while(!(TWCR & (1<<TWINT)));
  16.  
  17. //Проверка статуса
  18. if((TWSR & 0xF8) != 0x08)
  19. return FALSE;
  20.  
  21. //Now write SLA+W
  22. //EEPROM @ 00h
  23. TWDR=0b10100000;
  24.  
  25. //Инициализация передачи
  26. TWCR=(1<<TWINT)|(1<<TWEN);
  27.  
  28. //Poll Till Done
  29. while(!(TWCR & (1<<TWINT)));
  30.  
  31. }while((TWSR & 0xF8) != 0x18);
  32.  
  33. //Now write ADDRH
  34. TWDR=(address>>8);
  35.  
  36. //Инициализация передачи
  37. TWCR=(1<<TWINT)|(1<<TWEN);
  38.  
  39. //Poll Till Done
  40. while(!(TWCR & (1<<TWINT)));
  41.  
  42. //Проверка статуса
  43. if((TWSR & 0xF8) != 0x28)
  44. return FALSE;
  45.  
  46. //Now write ADDRL
  47. TWDR=(address);
  48.  
  49. //Initiate Transfer
  50. TWCR=(1<<TWINT)|(1<<TWEN);
  51.  
  52. //Poll Till Done
  53. while(!(TWCR & (1<<TWINT)));
  54.  
  55. //Check status
  56. if((TWSR & 0xF8) != 0x28)
  57. return FALSE;
  58.  
  59. //Now write DATA
  60. TWDR=(data);
  61.  
  62. //Initiate Transfer
  63. TWCR=(1<<TWINT)|(1<<TWEN);
  64.  
  65. //Poll Till Done
  66. while(!(TWCR & (1<<TWINT)));
  67.  
  68. //Check status
  69. if((TWSR & 0xF8) != 0x28)
  70. return FALSE;
  71.  
  72. //Put Stop Condition on bus
  73. TWCR=(1<<TWINT)|(1<<TWEN)|(1<<TWSTO);
  74.  
  75. //Wait for STOP to finish
  76. while(TWCR & (1<<TWSTO));
  77.  
  78. //Wait untill Writing is complete
  79. _delay_ms(12);
  80.  
  81. //Return TRUE
  82. return TRUE;
  83. }
  84.  
  85. uint8_t EEReadByte(uint16_t address)
  86. { uint8_t data;
  87. //Initiate a Dummy Write Sequence to start Random Read
  88. do {
  89. //Put Start Condition on TWI Bus
  90. TWCR=(1<<TWINT)|(1<<TWSTA)|(1<<TWEN);
  91.  
  92. //Poll Till Done
  93. while(!(TWCR & (1<<TWINT)));
  94.  
  95. //Check status
  96. if((TWSR & 0xF8) != 0x08)
  97. return FALSE;
  98.  
  99. //Now write SLA+W
  100. //EEPROM @ 00h
  101. TWDR=0b10100000;
  102.  
  103. //Initiate Transfer
  104. TWCR=(1<<TWINT)|(1<<TWEN);
  105.  
  106. //Poll Till Done
  107. while(!(TWCR & (1<<TWINT)));
  108.  
  109. }while((TWSR & 0xF8) != 0x18);
  110.  
  111. //Now write ADDRH
  112. TWDR=(address>>8);
  113.  
  114. //Initiate Transfer
  115. TWCR=(1<<TWINT)|(1<<TWEN);
  116.  
  117. //Poll Till Done
  118. while(!(TWCR & (1<<TWINT)));
  119.  
  120. //Check status
  121. if((TWSR & 0xF8) != 0x28)
  122. return FALSE;
  123.  
  124. //Now write ADDRL
  125. TWDR=(address);
  126.  
  127. //Initiate Transfer
  128. TWCR=(1<<TWINT)|(1<<TWEN);
  129.  
  130. //Poll Till Done
  131. while(!(TWCR & (1<<TWINT)));
  132.  
  133. //Check status
  134. if((TWSR & 0xF8) != 0x28)
  135. return FALSE;
  136.  
  137. //*************************DUMMY WRITE SEQUENCE END **********************
  138.  
  139. //Put Start Condition on TWI Bus
  140. TWCR=(1<<TWINT)|(1<<TWSTA)|(1<<TWEN);
  141.  
  142. //Poll Till Done
  143. while(!(TWCR & (1<<TWINT)));
  144.  
  145. //Check status
  146. if((TWSR & 0xF8) != 0x10)
  147. return FALSE;
  148.  
  149. //Now write SLA+R
  150. //EEPROM @ 00h
  151. TWDR=0b10100001;
  152.  
  153. //Initiate Transfer
  154. TWCR=(1<<TWINT)|(1<<TWEN);
  155.  
  156. //Poll Till Done
  157. while(!(TWCR & (1<<TWINT)));
  158.  
  159. //Check status
  160. if((TWSR & 0xF8) != 0x40)
  161. return FALSE;
  162.  
  163. //Now enable Reception of data by clearing TWINT
  164. TWCR=(1<<TWINT)|(1<<TWEN);
  165.  
  166. //Ожидание до конца выполнения
  167. while(!(TWCR & (1<<TWINT)));
  168.  
  169. //Проверка статуса
  170. if((TWSR & 0xF8) != 0x58)
  171. return FALSE;
  172.  
  173. //Чтение данных
  174. data=TWDR;
  175.  
  176. //Put Stop Condition on bus
  177. TWCR=(1<<TWINT)|(1<<TWEN)|(1<<TWSTO);
  178.  
  179. //Wait for STOP to finish
  180. while(TWCR & (1<<TWSTO));
  181. //Return TRUE
  182. return data;
  183. }

Содержание файла-конфигурации 24c64.h:

  1. #ifndef _24C64_H_
  2. #define _24C64_H_
  3.  
  4. #define FALSE 0
  5. #define TRUE 1
  6.  
  7. //Прототипы функций
  8. void EEOpen();//Функция инициализации памяти
  9. uint8_t EEWriteByte(uint16_t,uint8_t);//Функция записи байта в память
  10. uint8_t EEReadByte(uint16_t address);//Функция чтения байта из памяти
  11.  
  12. #endif

Файл atmega8_eeprom_read.c:

  1. #include <avr/io.h>//библиотека ввода/вывода
  2. #include "util/delay.h"//библиотека задержки
  3. #include "24c64.h"//подключаем конфигурационный файл
  4. #include "24c64.c"//подключаем файл с набором функций для записи/чтения в EEPROM
  5.  
  6. unsigned char b;//переменная для отправки и приема данных по USART
  7. unsigned int address;//переменная адреса
  8.  
  9. //Функция инициализации модуля USART
  10. void USART_Init( unsigned int ubrr)
  11. {
  12. /* Устанавливаем baud rate */
  13. UBRRH = (unsigned char)(ubrr>>8);
  14. UBRRL = (unsigned char)ubrr;
  15. /* Разрешаем прием и передачу */
  16. UCSRB = (1<<RXEN)|(1<<TXEN);
  17. /* устанавливаем формат данных: 8 бит данных, 2 стоп бита*/
  18. UCSRC=0x86;//
  19. UCSRC = (1<<URSEL)|(1<<USBS)|(3<<UCSZ0);
  20. }
  21.  
  22. void USART_Transmit( unsigned char data ) //Функция отправки данных по USART
  23. { while ( !(UCSRA & (1<<UDRE)) ); //Ожидание опустошения буфера приема
  24. UDR = data; //Начало передачи данных
  25. }
  26.  
  27. void eeread (void)//Функция чтения данных из памяти EEPROM
  28. { for(address=0; address<25; address++)//цикл с адресами данных
  29. {b = EEReadByte(address);//присваиваем переменной b прочитанный байт данных
  30. USART_Transmit(b);//Передаем прочитанный байт по USART
  31. }
  32. USART_Transmit(0x0d);//переход в начало строки
  33. USART_Transmit(0x0a);//переход на новую строку
  34. }
  35.  
  36. int main(void)//Основная программа
  37. { //Инициализация EEPROM
  38. EEOpen();
  39. _delay_ms (15);//Небольшая задержка
  40.  
  41. //Конфигурируем модуль USART на скорость 115200кБит/с при частоте кварца 16МГц
  42. USART_Init (8);//115200 16MHz
  43.  
  44. while(1)//Вечный цикл
  45. {eeread ();//Выполняем чтение заданной области памяти из микросхемы EEPROM
  46. }
  47. }

Программа выполняет чтение данных из микросхемы внешней EEPROM и отправляет прочитанные значения ячеек по порту USART, которые можно легко прочитать подключив конвертер уровней на MAX232 и запустив программу Terminal RS232. Адрес, до которого выполнять чтение, задается в части кода:

  1. for(address=0;address<25;address++)
  2. {
  3. b = EEReadByte(address);
  4. }

0 -начальный адрес ячейки,
25 - значение номера ячейки памяти, до которой выполнять чтение. При желании всегда можно изменить, или задать это значение через переменную, которая будет изменяться в зависимости от того, какую область памяти необходимо считать.

Так же в библиотеке для работы с внешней памятью EEPROM есть функция записи, вызывается она следующим образом:

  1. for(address=0; address<500; address++)//цикл с пределами памяти
  2. {
  3. if(EEWriteByte(address,a)==0)//проверка возможности записи
  4. {//Write Failed
  5. ...//Команды для выполнения если запись не удалась
  6. break;
  7. }
  8. }

Таким образом в данной статье есть все необходимые программы для осуществления чтения/записи с внешней микросхемы памяти EEPROM из серии 24CXX. Удачи!

А как записать по i2c

А как записать по i2c в eeprom по адресу 0x7D00(32000)?
eeprom 64 Kb.

int address = 0x7D00; int a =

int address = 0x7D00;
int a = 32000;
EEWriteByte(address,a);

Вот так.

>>Микросхемы EEPROM имеют так

>>Микросхемы EEPROM имеют так называемый Lock-бит.
>>Этот бит защищает микросхему от случайного, или преднамеренного стирания. Записывается он единажды, на заводе, где производят устройство.

Откуда такая информация если не секрет? Я знаю что у них есть нога "только чтение" ни про какой лок бит не слыхивал.

Я так скажу, имел опыт работы

Я так скажу, имел опыт работы с микрой вытянутой из модема старого. Подключил так же как и перед этим обычную, новую микру eeprom. Результат: получилось только прочитать содержимое. Все попытки записать заканчивались неудачей. По сему, микра имеет бит, который отвечает за запись!

То ты наверно вытянул память

То ты наверно вытянул память типа ROM, которая записывается один раз на заводе. Игрался с похожей, вытянутой с мертвого CD-ROM'а.

Добрый вечер! А возможно

Добрый вечер!
А возможно переделать данный проект не с работой 24схх ,а с другим контроллером?
Или полностью придется все переделывать?

Вопрос как-то не корректно

Вопрос как-то не корректно сформулирован.
Контроллер там может быть любой начиная с ATmega8, а микросхема может быть любая из серии 24Cxx