Запись и чтение EEPROM в AVR

Запись и чтение EEPROM данных типа unsigned int, unsigned long, float и других типов данных, размером более 8 бит, т.е. тех данных, которые размером более одного байта.
Все примеры здесь указаны для языка СИ.

Сначала приведем основной пример для чтения и записи типа unsigned char, т.к. это будут основные функции, на основе которых будет происходить чтение и запись ячеек еепром.

/* ------------------------------------------------------------------------ */
// Функции чтения и записи в еепром одного байта.
/* ------------------------------------------------------------------------ */

void EEPROM_write (unsigned int uiAddress, unsigned char ucData)
{
    unsigned char cSREG;

    /* Wait for completion of previous write */
    while(EECR & (1<<EEWE));
    /* Set up address and data registers */
    EEAR = uiAddress;
    EEDR = ucData;
    
    cSREG = SREG; /* store SREG value */
    /* disable interrupts during timed sequence */
    
    #if __CODEVISIONAVR__ /* if CodeVisionAVR */
        #asm("cli");
    #else
        asm("cli");
    #endif
    
    /* Write logical one to EEMWE */
    EECR |= (1<<EEMWE);
    /* Start eeprom write by setting EEWE */
    EECR |= (1<<EEWE);
    
    SREG = cSREG; /* restore SREG value (I-bit) */
}

unsigned char EEPROM_read(unsigned int uiAddress)
{
    /* Wait for completion of previous write */
    while(EECR & (1<<EEWE));
    /* Set up address register */
    EEAR = uiAddress;
    /* Start eeprom read by writing EERE */
    EECR |= (1<<EERE);
    /* Return data from data register */
    return EEDR;
}

Эти функции без проблем позволяют считывать из Еепром данные или наоборот, записывать какие либо данные в Еепром. Этих функций в целом достаточно для записи или чтения любых типов данных, просто используя циклы, когда количество байтов более одного. Но есть тут одно неудобство, при таком подходе (к примеру для записи), если тип записываемой переменной более одного char, например int, а это уже два байта, то придется каждый раз подготавливать данные в самом коде программы и потом в цикле или построчно (побайтно) уже производить запись.

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

Пример записи числа unsigned int в Eeprom без цикла

/* ------------------------------------------------------------------------ */
// Запись в Eeprom числа unsigned int (без цикла).
/* ------------------------------------------------------------------------ */

unsigned int addr;
unsigned int TimeStopTrunk;

addr = 0x3F0;
TimeStopTrunk = 0xAABB;

// Пишем в Eeprom 2 байта.
EEPROM_write((unsigned int) addr,(TimeStopTrunk>>8));        // Первый байт слова
EEPROM_write((unsigned int) addr+1,(TimeStopTrunk&0xFF));    // Второй байт слова

Пример записи числа unsigned int в Eeprom с циклом

/* ------------------------------------------------------------------------ */
// Запись в Eeprom числа unsigned int (в цикле).
/* ------------------------------------------------------------------------ */

unsigned int addr;
unsigned int TimeStopTrunk[2];
unsigned char i;

addr = 0x3F0;
TimeStopTrunk = 0xAABB;

writed[0]=TimeStopTrunk>>8;   // Первый байт слова
writed[1]=TimeStopTrunk&0xFF; // Второй байт слова

// Пишем в Eeprom 2 байта.

for( i = 0; i < 2; i++ )
{
	EEPROM_write(addr+i, writed[i]);
}

На самом деле вариаций таких примером немалое количество, все их приводить конечно мы не будем, но вы понимаете, насколько можно замусорить исполняемый код своей программы? А сколько неудобств каждый раз объявлять переменные, обрабатывать и т.д. и т.п. А не удобнее ли все делать одной строчкой? Вот к этому мы и должны прийти!

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

Итак, вот упрощенная реализация чтения и записи числа int

/* ------------------------------------------------------------------------ */
// Чтение числа int из еепром.
/* ------------------------------------------------------------------------ */

unsigned int EEPROM_read_int(unsigned int addr) {    
  unsigned char buf[2];
  unsigned char i;
  
  for( i = 0; i < 2; i++ ) buf[i] = EEPROM_read(addr+i);
  unsigned int &num = (unsigned int&)buf;
  return num;
}

/* ------------------------------------------------------------------------ */
// Запись числа int в еепром.
/* ------------------------------------------------------------------------ */

void EEPROM_write_int(unsigned int addr, unsigned int num) {
  unsigned char buf[2];
  unsigned char i;
  
  (unsigned int&)buf = num;
  for( i = 0; i < 2; i++ ) EEPROM_write(addr+i, buf[i]);
}

Теперь все основные действия мы будем совершать всего одной строкой

/* ------------------------------------------------------------------------ */
// Чтение числа int из еепром.
/* ------------------------------------------------------------------------ */

EEPROM_read_int((unsigned int) addr);

/* ------------------------------------------------------------------------ */
// Запись числа int в еепром.
/* ------------------------------------------------------------------------ */

EEPROM_write_int((unsigned int) addr,dataint);

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

Таким образом, на основе этих функций мы можем создать новые функции и для других типов, например unsigned long или float. А также было бы очень неплохо в функции записи реализовать проверку на запись одинакового значения, и производить запись в ячейку памяти только в случае, если новые и старые данные различаются. Ну зачем нам лишний раз повторно переписывать ячейки в Eeprom, верно?

30 комментариев к “Запись и чтение EEPROM в AVR”

  1. В AVR Studio GCC твои функции EEPROM_read_int EEPROM_write_int выдают ошибки
    Короче код твой херня не проверенная
    1. Все проверено! И кто сказал, что этот код должен работать в AVR Studio?. Я не пишу программы в AVR Studio, код прекрасно работает, проверен и написан для IAR Embedded Workbench. Заметки сделаны для себя или для людей, которые понимают, что к чему. Предполагаю, у Вас из-за различия определения типов данных, у каждого компилятора могут быть свои «имена», а также для некоторой совместимости можно самому переопределить типы данных, что я и сделал для совместимости с CodeVision AVR. Но никак не для AVR Studio. Впрочем, может быть и иная несовместимость, поэтому открываем даташит на любой проц AVR, и там есть готовые примеры кода записи и чтения еепром именно для AVR Studio.
    2. И не надо ссылки на казино давать. Будь любезен нормально себя вести, если хочешь впитать полезные советы от людей.
  2. /* ------------------------------------------------------------------------ */
    // Чтение числа int из еепром.
    /* ------------------------------------------------------------------------ */
    EEPROM_read_int((unsigned int) addr,dataint);

    Тут dataint лишнее.
  3. Привет iSavaDev Помогите разобраться с кодом записи
    компилятор codevisionavr v3.12 ругается на эту строку: unsigned int &data = (unsigned int)&buf; -не допустимый символ.
    в чем может быть проблема?

    /* чтение из памяти EEPROM */
    unsigned int eeplc24_read(unsigned int addr) {
    unsigned char buf[2];
    unsigned char i;
    unsigned int data;

    for( i = 0; i < 2; i++ ) buf[i] = eeplc24_read(addr+i);
    unsigned int &data = (unsigned int)&buf;

    i2c_start();
    i2c_write(EEPROM_BUS_ADDRESS);
    i2c_write(addr);
    i2c_start();
    i2c_write(EEPROM_BUS_ADDRESS | 1);
    data=i2c_read(0);
    i2c_stop();
    return data;
    }
    1. неизвестный символ data
      и по прежнему unsigned int &data = (unsigned int&)buf; -не допустимый символ.
    2. Я проверил в IAR, ошибка ушла после того, как я закомментировал строку по совету выше. Возможно компилятор CodeVision по иному требует, ругается на знак ссылки при объявлении переменной, т.е., попробуйте убрать символ амперсанд перед data.
      unsigned int data = (unsigned int&)buf;
    3. Давно я не кодил)) Вот советы:

      // Здесь объявляем переменную data.
      unsigned int data;

      // Наполняем буфер
      for( i = 0; i < 2; i++ ) buf[i] = eeplc24_read(addr+i);

      // Здесь заполняем data
      data = (unsigned int) &buf;

      // Или в одной строке объявляем переменную и сразу ее наполняем.
      unsigned int data = (unsigned int) &buf;

      // Или еще проще, если не использовать переменную data, а сразу работать с буфером, то есть просто удалить лишний код и использовать или возвращать напрямую переменную buf.
    4. ошибку выкидывает:
      unsigned int eeplc24_read(unsigned int addr) function return size mismatch with entry 'eeplc24_read'
      что делать...
    5. У меня на Windows XP (32 бита) нет ошибки в CodeVision 2.05 и в IAR тоже нет. Да, была изначально ошибка в Вашем коде на указанной строке, но по моему совету выше она ушла. Других ошибок у меня нет. Версии CodeVision v3.12 у меня тоже нет, чтобы проверить. Надо разбираться в Вашем коде, а точнее скорее всего там, где и как вы используете Вашу функцию eeplc24_read(). Возможно проблема в типе данных с ошибкой mismatch..
  4. Вылезла ошибка при записи:

    /* запись в память EEPROM */
    void eeplc24_write(unsigned int addr, unsigned int data) {
    unsigned char buf[2];
    unsigned char i;

    (unsigned int)&buf = data; the expression must be a modifiable lvalue
    for( i = 0; i < 2; i++ ) eeplc24_write(addr+i, buf[i]);

    i2c_start();
    i2c_write(EEPROM_BUS_ADDRESS);
    i2c_write(addr);
    i2c_write(data);
    i2c_stop();
    delay_ms(7); /* 10ms delay to complete the write operation */
    }
  5. //Чтение\ зацикливается, постоянно крутится в цикле for( i = 0; i < 2; i++ ) buf[i] = eeplc24_read(addr+i);

    /* чтение из памяти EEPROM */
    unsigned int eeplc24_read(unsigned int addr) {
    unsigned char buf[2];
    unsigned char i;

    unsigned int data = (unsigned int)& buf;
    for( i = 0; i < 2; i++ ) buf[i] = eeplc24_read(addr+i);
    i2c_start();
    i2c_write(EEPROM_BUS_ADDRESS);
    i2c_write(addr);
    i2c_start();
    i2c_write(EEPROM_BUS_ADDRESS | 1);
    data=i2c_read(0);
    i2c_stop();
    return data;
    }

    void main(void)
    {
    Здесь читаем, и записываем в переменную M1_side_A=eeplc24_read(0);
    }
    1. А зачем вы внутри функции вызываете еще раз себя (эту же функцию)? Таким образом Вы уходите в бесконечную рекурсию. Ваша задача реализовать разные функции по задачам и в основном цикле вызывать уже нужное.
    2. Я к сожалению не могу тут давать уроки программирования, это общие принципы для любого языка программирования. Почитайте в сети, что такое рекурсия и все поймете. Вы внутри функции eeplc24_read() вызываете эту же функцию, а это уход в бесконечный цикл.
    3. Я уже два раза говорил, в чем ошибка. Подробнее как это сделать, даже не знаю. Попробую и повторю, Вы создали функцию eeplc24_read(). Так вот в коде этой функции нельзя вызывать эту же функцию, то есть саму себя. Точнее можно, но не в Вашем случае. Перепишите код так, чтобы процедура чтения еепром была в отдельной функции. Создайте две функции, в одной будете получать данные, в другой записывать полученные данные. И в основном цикле main() уже вызывайте их любым способом, хоть создайте третью функцию.

      Вот пример (лишнее убрал). В моем случае у меня разные! функции (названия)
      EEPROM_read_int() и EEPROM_read().

      unsigned int EEPROM_read_int(unsigned int addr) {
      for( i = 0; i < 2; i++ ) buf[i] = EEPROM_read(addr+i);
      }

      Посмотрите на мои примеры в публикации, сравните с Вашими. Ваша ошибка, что Вы вызываете eeplc24_read(), а внутри этой функции еще раз eeplc24_read(). Все, Вы уходите в бесконечный цикл. Ваша функция убьет Вам еепром от огромного количества перезаписи еепром в бесконечном цикле.
    4. здравствуйте iSavaDev. Вы можете сделать под заказ библиотеку под микросхемы еепром 24LC256 для записи: 2, 4 байт чисел? Сколько это будет стоить?
      Компилятор:Codevisionavr.
    5. Здравствуйте. К сожалению, физически нет времени. Да и библиотека уже встроена в CodeVisionAVR, у Вас почти все готово для создания своих функций чтения и записи. Вам помогли на радиокот, разобрались?
    6. оказывается не так все просто ! Люди опытные затрудняются написать библиотеку 2 байта чтение/запись.
      А куда мне -я начинающий. "Опыт приходит со временем ".
  6. Здравствуйте! Не могли бы подсказать контакты Савелия Северного по прошивке контроллера на Дайсон? Спасибо.
  7. доброго времени суток. можно узнать как с помощью данных функций можно реализовать чтение с одной EEPROM и запись полученных данных в другую? например мне необходимо считать первые 256 бит с главной внешней EEPROM, сохранить все денные в массиве и записать данный массив в другую внешнюю EEPRom. Надеюсь на ваш скорый ответ
    1. Добрый вечер. В примерах выше используются штатные функции чтения и записи внутренней еепром. Для внешней еепром нужно либо подключать дополнительные библиотеки либо эти функции создавать самому. Если внешняя еепром работает по протоколу i2c, то по этому протоколу и получаем данные и записываем, смена еепром задается адресацией. Если Вам это незнакомо, я к сожалению не смогу тут расписать подробнее. Дело это не сложное, общий принцип един: start_i2c(), задаём адрес устройства, далее команды передачи или получения данных, указав стартовый адрес для нужной области еепром, и в конце stop_i2c().

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

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