PIC Урок 11. Внутренняя энергонезависимая память EEPROM. Часть 1



Очень многие знают, что в контроллерах PIC помимо основной оперативной памяти, а также памяти для хранения прошивки существует ещё и энергонезависимая память типа EEPROM.

Данная память сделана по технологии электрического стирания информации, что в отличие от её предшественника EPROM, в котором стирание производилось только при помощи ультрафиолетовых лучей, позволило использовать данный тип памяти практически повсеместно. Как мы знаем, ещё существует энергонезависимая память типа Flash, которая стоит намного дешевле, но у которой также есть существенный минус. Там невозможно стереть отдельный байт, стирание производится только блоками, что не совсем удобно в некоторых случаях, особенно когда информации требуется хранить немного, и информация данная представляет собой небольшие настроечные параметры. Поэтому нам стоит также остановиться на данном типе памяти. И причем не только из-за того, что он присутствует в контроллере, а из-за того, что это очень удобно для хранения некоторых величин, которые нужны нам будут даже после того, как контроллер потерял питание.

По идее, с данной памятью мы должны были познакомиться в более ранних занятиях, так как в технической документации на наши микроконтроллеры она находится в одной из первых частей, но мне хотелось для лучшего понимания и запоминания материала показать работу с EEPROM как-то наглядно. Мы подключали с вами четырёхразрядный индикатор, но четырёх разрядов для полноценного показа работы с энергонезависимой памятью, я думаю, мягко говоря, недостаточно, но после прошлого занятия, в котором мы изучили работу с 80-символьным дисплеем LCD, мы можем вполне оценить работу с памятью EEPROM.

Работать мы также будем с контроллером PIC16F877A, расположенном на удобной отладочной плате. Судя по технической документации на данный микроконтроллер, памяти EEPROM у него 256 байт. Этого вроде бы по нынешним меркам не так много, но вполне достаточно, чтобы хранить какую-то информацию, которая должна у нас остаться после отключения питания.

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

Существуют два регистра для записи и чтения памяти EEPROM. Это регистр, в котором хранятся данные, — EEDATA, а также регистр, в который мы посылаем адрес перед записью или чтения определённого байта, — EEADR. Также существует регистр управления EECON1, в котором находятся определённые биты настройки работы с памятью EEPROM, а также есть нефизический регистр EECON2, который мы используем для защиты от случайной записи данной памяти.

Также с помощью определённых конфигурационных битов мы можем защитить память EEPROM от стирания и перезаписи программатором.

Теперь немного подробнее про регистр управления EECON1

 

 

Теперь о назначении определённых битов данного регистра.

EEPGD — бит доступа к памяти (относится к памяти Flash)

1 — доступ к памяти для программы,

0 — доступ к памяти для данных.

WRERR (EEPROM Error Flag bit) — флаг ошибки записи данных в память EEPROM

1 — операция записи данных прервана (случился сброс либо по сигналу MCLR, либо по переполнению WDT в нормальном режиме),

0 — операция записи данных завершена.

WREN (EEPROM Write Enable bit) — бит разрешения записи данных в память EEPROM

1 — запись данных разрешена,

0 — запись данных запрещена.

WR (Write Control bit) — включение (инициализация) записи данных в память EEPROM

1 — инициализация цикла записи памяти EEPROM. Включается программно,

0 — цикл записи EEPROM закончен. Установка в 0 (сброс) осуществляется только аппаратным путём. Программно данный флаг сбросить невозможно.

RD (Read Control bit) — включение (инициализация) чтение данных из памяти EEPROM.

1 — инициализация цикла чтения памяти EEPROM. Включается программно,

0 — цикл чтения EEPROM закончен. Установка в 0 (сброс) осуществляется только аппаратным путём. Программно данный флаг сбросить также невозможно.

 

Чтение байта из памяти EEPROM производится следующим образом.

Первым делом мы должны удостовериться, что биты RD и WR регистра EECON1 у нас сброшены, то есть у нас в данный момент все циклы записи/чтения памяти EEPROM завершены. Затем мы записываем в регистр EEADR адрес считываемого байта, затем устанавливаем в 1 бит RD регистра EECON1 и считываем значение регистра EEDATA.

А вот запись байта в память EEPROM в целях безопасности происходит несколько посложнее.

Сначала мы также убеждаемся в сброшенном бите WR, затем разрешаем запись с помощью установки бита WREN регистра EECON1. Затем желательно запретить прерывания с помощью сброса бита GIE регистра INTCON, перед этим сохранив его значение, а то вдруг он уже был сброшен. А вот теперь мы делаем интересную вещь. Мы последовательно передаём байты 0x55 и 0xAA в регистр ЕЕСОN2. И только после этого мы пишем данные в память EEPROM, устанавливая бит инициализации записи WR. Затем мы возвращаем значение бита GIE в регистр INTCON, разрешив тем самым глобальные прерывания, если они до этого были разрешены. Потом мы сбрасываем бит WREN.

Вот и всё.

Чтобы нам теперь данную теорию понять и закрепить, мы должны отработать это на практике.

Поэтому создадим проект с именем EEPROM_LCD на основе проекта прошлого занятия LCD2004_8BIT.

Подключим нашу схему вместе с программатором и дисплеем, блок питания пока в целях безопасности не подключаем.

Откроем данный проект в MPLAB X, зайдём в его настройки и убедимся, что у нас схема не будет питаться от программатора и сохраним настройки проекта.

Теперь мы можем смело подключить блок питания, также попытаться собрать наш проект и прошить его в контроллер. Работоспособность нашего кода мы поймём по появившимся строкам на дисплее

 

 

 

Создадим каркас нашей будущей библиотеки для работы с памятью EEPROM с помощью двух файлов EEPROM.h и EEPROM.c следующего стандартного содержания

 

EEPROM.h:

#ifndef _EEPROM_H_H

#define _EEPROM_H_H

//——————————————————

#include <xc.h> // include processor files - each processor file is guarded.

#include <stdio.h>

#include <stdlib.h>

//-----------------------------------------------------

//-----------------------------------------------------

#endif /* _EEPROM_H */

 

EEPROM.c:

#include "EEPROM.h"

//-----------------------------------------------------

 

Также нашу библиотеку, а заодно и строковую, подключим в файле main.h

 

#include "lcd.h"

#include "EEPROM.h"

#include <string.h>

 

Вернёмся в файл EEPROM.c и напишем функцию записи байта в память EEPROM по определённому адресу, руководствуясь объяснению, как это делается, написанному выше. Заодно сразу добавим и функцию чтения байта по определённому адресу

 

//-----------------------------------------------------

void EEPROM_WriteByte (unsigned char addr, unsigned char dt)

{

  unsigned char status;

  while(WR);

  EEADR = addr;

  EEDATA = dt;

  WREN=1;

  status = GIE;

  GIE = 0;

  EECON2 = 0x55;

  EECON2 = 0xAA;

  WR=1;

  GIE = status;

  WREN=0;

}

//-----------------------------------------------------

unsigned char EEPROM_ReadByte(unsigned char addr)

{

  while(RD || WR);

  EEADR=addr;

  RD = 1;

  return EEDATA;

}

//-----------------------------------------------------

 

Также нам будет интересно писать и читать данные других типов.

 

 

Добавим функции записи и чтения двухбайтовой целочисленной беззнаковой величины в память EEPROM и из неё

 

//-----------------------------------------------------

void EEPROM_WriteWord(unsigned char addr, unsigned int ucData)

{

  EEPROM_WriteByte(addr, (unsigned char) ucData);

  unsigned char dt = ucData>>8;

  EEPROM_WriteByte(addr+1, dt);

}

//-----------------------------------------------------

unsigned int EEPROM_ReadWord(unsigned char addr)

{

  unsigned int dt = EEPROM_ReadByte(addr+1)*256;

  dt += EEPROM_ReadByte(addr);

  return dt;

}

//-----------------------------------------------------

 

Здесь у нас всё ещё проще. Мы читаем и пишем раздельно старший и младший байты, пользуясь уже добавленными функциями чтения и записи байтов.

Теперь добавим ещё функции записи и чтения четырёхбайтовых величин (двойных слов)

 

//-----------------------------------------------------

void EEPROM_WriteDword(unsigned char addr, unsigned long ucData)

{

  EEPROM_WriteWord(addr, (unsigned int) ucData);

  unsigned int dt = ucData>>16;

  EEPROM_WriteWord(addr+2, dt);

}

//-----------------------------------------------------

unsigned long EEPROM_ReadDword(unsigned char addr)

{

  unsigned long dt = EEPROM_ReadWord(addr+2)*65536;

  dt += EEPROM_ReadWord(addr);

  return dt;

}

//-----------------------------------------------------

 

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

Теперь давайте запишем строку символов по определённому адресу

 

//-----------------------------------------------------

void EEPROM_WriteString(unsigned char addr, char* str1)

{

  unsigned char n;

  for(n=0;str1[n]!='\0';n++)

  EEPROM_WriteByte(addr+n,str1[n]);

}

//-----------------------------------------------------

 

Здесь также всё элементарно. Мы, инкрементируя адрес, постепенно записываем наши байты. И так до встречи с нулём.

Функцию чтения строки по определённому адресу нас написать также не затруднит

 

//-----------------------------------------------------

void EEPROM_ReadString(unsigned char addr, char* str1, unsigned char sz)

{

  unsigned char i;

  for (i=0;i<sz;i++) str1[i] = EEPROM_ReadByte(addr+i);

  str1[i] = 0;

}

//-----------------------------------------------------

 

В данной функции мы используем ещё один входящий аргумент — это величина считываемой строки. Можно было также и читать строку до встречи с нулём, но этого я делать не стал, так как вдруг этого нуля мы там не обнаружим, и тогда мы рискуем записать данные в оперативную память за пределы нашего строкового массива, что не очень будет приятно. Поэтому будем считать, что мы знаем длину считываемой строки.

Если есть желание, то вы можете написать функцию именно для считывания строки неизвестной длины, но только знайте, что она там должна обязательно существовать.

Перейдём в файл EEPROM.h и добавим прототипы наших функций

 

#include <stdlib.h>

//-----------------------------------------------------

void EEPROM_WriteByte(unsigned char addr, unsigned char dt);

unsigned char EEPROM_ReadByte(unsigned char addr);

void EEPROM_WriteWord(unsigned char addr, unsigned int ucData);

unsigned int EEPROM_ReadWord(unsigned char addr);

void EEPROM_WriteDword(unsigned char addr, unsigned long ucData);

unsigned long EEPROM_ReadDword(unsigned char addr);

void EEPROM_WriteString(unsigned char addr, char* str1);

void EEPROM_ReadString(unsigned char addr, char* str1, unsigned char sz);

//-----------------------------------------------------

 

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

 

 

Предыдущий урок Программирование МК PIC Следующая часть

 

Купить программатор (неоригинальный) можно здесь: PICKit3

Купить программатор (оригинальный) можно здесь: PICKit3 original

Отладочную плату PIC Open18F4520-16F877A можно приобрести здесь: PIC Open18F4520-16F877A

Дисплей LCD 20×4 можно приобрести тут: Дисплей LCD 20×4

 

 

Смотреть ВИДЕОУРОК (нажмите на картинку)

 

PIC Внутренняя энергонезависимая память EEPROM

4 комментария на “PIC Урок 11. Внутренняя энергонезависимая память EEPROM. Часть 1
  1. Для PIC18FXX2, в частности PIC18F242 для чтения байта из EEPROM правильным будет вот такой код:

    unsigned char EEPROM_ReadByte(unsigned char addr)
    {
    char eeprom_data;

    while(RD || WR);
    GIE = 0;
    EEADR = addr;
    EEPGD = 0;
    CFGS = 0;
    RD = 1;
    eeprom_data = EEDATA;
    GIE = 1;

    return eeprom_data;
    }

    или в ассемблере примерно так:

    MOVWF EEADR
    BCF EECON1,EEPGD
    BCF EECON1,CFGS
    BSF EECON1,RD
    MOVF EEDATA,W

    В противном случае в Протеусе работать будет, а в реальном устройстве часто будет происходить ошибка, при которой всегда читается 0x00. Особенно часто она возникает, если напряжение питания при включении нарастает плавно, а не мгновенно, например при включении блока питания в сеть. Никакие PWRT и BOR не помогут. См. соответствующую ERRDATA на контроллер.

  2. Артур:

    А можно, объяснить работу EEPROM на примере различных режимов работы светодиодов. На примере LCD чрезвычайно сложная запись кода и ни фига не укладывается в голове принцип работы. Что-то пишу в mane.c — программа ругается, код не собирается. Может я что-то не понимаю, может объясняют мне не очень.

  3. Артур:

    Целый месяц постигал. Не без вашей, конечно помощи. Благодарю.

  4. ton19:

    А в симуляторе можно увидеть запись в EECON2? У меня не записывается видятся нули. Программа в симуляторе не сбрасывает WR в «0».

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

*