Рейтинг@Mail.ru
Flash-память под микроскопом
Flash-память под микроскопом

Чтение и запись флеш-памяти с помощью Arduino на примере микросхемы 25L8005

11 комментарии Arduino
Print Friendly, PDF & Email
В статье мы рассмотрим, как записывать во флеш-память и читать из флеш-памяти с помощью Arduino на примере микросхемы 25L8005.

1Микросхема флеш-памяти25L8005

Микросхема флеш-памяти (или ПЗУ) 25L8005 изготавливается в разных корпусах. Но как правило, ввиду малого размера, удобнее использовать микросхемы поверхностного монтажа. У меня как раз такая. Она выполнена в корпусе типа SOP 8L.

Поэтому сначала нужно сделать так, чтобы мы могли с лёгкостью подключиться к микросхеме флеш-памяти. Для этого можно воспользоваться либо специальным переходником, к которому придётся припаять микросхему, либо (что предпочтительнее) использовать панель с нулевым усилением (т.н. панель ZIF, купить можно на Али).

Так выглядит наша микросхема ПЗУ, припаянная к плате-переходнику с DIP-выводами:

Флеш-память припаяна к плате-переходнику
Флеш-память припаяна к плате-переходнику

А вот так выглядит микросхема флеш-памяти в ZIF-панели:

Флеш-память в переходной панели с нулевым усилением
Флеш-память в переходной панели с нулевым усилением

И под микроскопом:

Флеш-память в панели с нулевым усилением под микроскопом
Флеш-память в панели с нулевым усилением под микроскопом

Купить ПЗУшку можно, например, в Китае.

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

2Подключение Arduino к микросхеме flash-памяти 25L8005

Назначение выводов микросхемы ПЗУ, приведённое в техническом описании на микросхему (datasheet), следующее:

Назначение выводов микросхемы флеш-памяти 25L8005
Назначение выводов микросхемы флеш-памяти 25L8005

Кстати, datasheet на микросхему флеш-памяти 25L8005 можно скачать в конце статьи.

Будем использовать для программирования флэш-памяти интерфейс SPI, поэтому подключимся к стандартным SPI выводам Arduino:

Вывод микросхемы флеш-памятиВывод платы Arduino
CS#цифровой пин 10
SIцифровой пин 11
SOцифровой пин 12
SCLKцифровой пин 13
WP#3V3
HOLD#3V3
VCC3V3
GNDGND

Здесь символом решётки # отмечены выводы, которые активируются логическим нулём.

Соберём электрическую схему подключения микросхемы флеш-памяти MX25L8005 к Arduino.

Схема подключения микросхемы флеш-памяти 25L8005 к Arduino
Схема подключения микросхемы флеш-памяти 25L8005 к Arduino

У меня ПЗУ на монтажной плате и подключённое к Arduino выглядит вот так:

Флеш-память на ZIF-панели, подключённая к Arduino
Флеш-память на ZIF-панели, подключённая к Arduino
Флеш-память на ZIF-панели, подключённая к Arduino
Флеш-память на ZIF-панели, подключённая к Arduino

3Очистка флеш-памяти с помощью Arduino

Перед тем как записывать данные в флеш-память, необходимо стереть тот сектор или страницу, в который мы собираемся записывать. Если записываемых данных немного (в нашем учебном примере это будут всего 16 байт), то достаточно стереть 1 сектор. Из документации на микросхему мы видим, что последовательность стирания такая: выставить разрешение на запись (1 байт), послать команду на стирание (1 байт) и адрес (3 байта), выставить запрет записи (1 байт). Данная последовательность приведена на рисунке ниже:

Диаграмма очистки одного сектора флеш-памяти 25L8005
Диаграмма очистки одного сектора флеш-памяти 25L8005

Именно это и делает приведённый ниже скетч:

#include <SPI.h>

const int SSPin = 10;
const byte WREN = 0x06;
const byte WRDI = 0x04;
const byte SER = 0x20;
const byte ADDR1 = 0;
const byte ADDR2 = 0;
const byte ADDR3 = 0;

void setup() {
  pinMode(SSPin, OUTPUT);
  SPI.begin();

  SPISettings mySet(100000, MSBFIRST, SPI_MODE0);
  
  // Выставление разрешения записи:
  SPI.beginTransaction(mySet);
  digitalWrite(SSPin, LOW);
  SPI.transfer(WREN);
  digitalWrite(SSPin, HIGH);
  SPI.endTransaction();

  // Очистка сектора:
  SPI.beginTransaction(mySet);
  digitalWrite(SSPin, LOW);
  SPI.transfer(SER);
  SPI.transfer(ADDR1);
  SPI.transfer(ADDR2);
  SPI.transfer(ADDR3);
  digitalWrite(SSPin, HIGH);
  SPI.endTransaction();
 
  // Выставление запрета записи:
  SPI.beginTransaction(mySet);
  digitalWrite(SSPin, LOW);
  SPI.transfer(WRDI);
  digitalWrite(SSPin, HIGH);
  SPI.endTransaction();
}

void loop() {
  // ничего не делаем в цикле
}

Загрузим этот скетч в Arduino с подключённой по приведённой выше схеме микросхемой памяти. После того как он отработал, флешка готова к записи.

4Запись данных в флеш-память с помощью Arduino

Теперь запишем на неё данные. Для примера возьмём небольшой массив из 16-ти байтов. Как видно из документации, для записи данных во флеш сначала нужно выставить разрешение на запись (1 байт), затем послать команду на запись (1 байт), передать начальный адрес (3 байта) и данные (в нашем примере 16 байт), а в конце выставить запрет записи (1 байт):

Диаграмма записи данных во флеш-память 25L8005
Диаграмма записи данных во флеш-память 25L8005

Напишем скетч, который записывает массив из 16-ти байт данных в ПЗУ:

#include <SPI.h>

const int SSPin = 10;
const byte WREN = 0x06;
const byte WRDI = 0x04;
const byte READ = 0x03;
const byte PP = 0x02;
const byte ADDR1 = 0;
const byte ADDR2 = 0;
const byte ADDR3 = 0;

void setup() {
  pinMode(SSPin, OUTPUT);
  SPI.begin();

  SPISettings mySet(100000, MSBFIRST, SPI_MODE0);
  
  // Выставление разрешения записи:
  SPI.beginTransaction(mySet);
  digitalWrite(SSPin, LOW);
  SPI.transfer(WREN);
  digitalWrite(SSPin, HIGH);
  SPI.endTransaction();

  // Запись массива данных в ПЗУ:
  SPI.beginTransaction(mySet);
  digitalWrite(SSPin, LOW);
  SPI.transfer(PP);
  SPI.transfer(ADDR1);
  SPI.transfer(ADDR2);
  SPI.transfer(ADDR3);
  byte data[] = {0x48,0x45,0x4c,0x4c,0x4f,0x2c,0x20,0x53,0x4f,0x4c,0x54,0x41,0x55,0x2e,0x52,0x55};
  for (int i=0; i<sizeof(data); i++) {
    SPI.transfer(data[i]);
  }
  digitalWrite(SSPin, HIGH);
  SPI.endTransaction();
  
  // Выставление запрета записи:
  SPI.beginTransaction(mySet);
  digitalWrite(SSPin, LOW);
  SPI.transfer(WRDI);
  digitalWrite(SSPin, HIGH);
  SPI.endTransaction();
}

void loop() {
  // ничего не делаем в цикле
}

Загрузим скетч в Arduino. Кстати, вот так выглядит на логическом анализаторе обмен по SPI между Arduino и ПЗУ 25L8005, когда выполняется данный скетч.

Временная диаграмма записи в ПЗУ массива данных по SPI
Временная диаграмма записи в ПЗУ массива данных по SPI

После выполнения данного скетча во флеш-память должен был записаться наш тестовый массив. Давайте проверим, так ли это.

4Чтение данных из флеш-памяти с помощью Arduino

Согласно документации, чтение из флешки выполняется посредством такой последовательности: отправка команды на чтение (1 байт), начальный адрес (3 байта), а далее запрашивается столько байтов, сколько хотим прочитать из ПЗУ. Собственно, мы будем передавать в ПЗУ 16 нулей. Так как SPI – синхронный интерфейс, нам в ответ вернутся 16 записанных в ПЗУ байтов. Вот такая диаграмма приводится в описании к микросхеме:

Диаграмма чтения данных из флеш-памяти 25L8005
Диаграмма чтения данных из флеш-памяти 25L8005

Напишем скетч для чтения наших заветных 16-ти байт из микросхемы флеш-памяти 25L8005:

#include <SPI.h>

const int SSPin = 10;
const byte READ = 0x03;
const byte ADDR1 = 0;
const byte ADDR2 = 0;
const byte ADDR3 = 0;

void setup() {
  Serial.begin(9600);
  pinMode(SSPin, OUTPUT);
  SPI.begin();  
}

void loop() { 
  SPISettings mySet(100000, MSBFIRST, SPI_MODE0);
  SPI.beginTransaction(mySet);
  digitalWrite(SSPin, LOW);
  SPI.transfer(READ); 
  SPI.transfer(ADDR1);
  SPI.transfer(ADDR2);
  SPI.transfer(ADDR3);
  for (int i=0; i<16; i++) {
    byte data = SPI.transfer(0);
    Serial.print((char)data);
  }
  Serial.println();
  digitalWrite(SSPin, HIGH);
  SPI.endTransaction();
  delay(1000); // повторяем чтение каждую секунду
}

Загрузим скетч в Arduino и откроем монитор последовательных портов. В мониторе, как и ожидалось, 1 раз в секунду будет выводиться наш массив, считанный из флеш-памяти с помощью Arduino.

Чтение из флеш-памяти с помощью Arduino и вывод в монитор последовательных портов
Чтение из флеш-памяти с помощью Arduino и вывод в монитор последовательных портов

Вот как выглядит чтение из ПЗУ, если подключиться к линиям SPI логическим анализатором:

Временная диаграмма чтения данных из ПЗУ по SPI
Временная диаграмма чтения данных из ПЗУ по SPI

Как видно, байты этого массива соответствуют кодам ASCII строки "HELLO, SOLTAU.RU", которые мы и записали в микросхему памяти 25L8005 :-)

Последнее изменениеСреда, 18 Декабрь 2019 18:53 Прочитано 32243 раз
Теги :

Поблагодарить автора:

Поделиться

Print Friendly, PDF & Email

11 комментарии

  • Гриша
    Гриша 17.12.2019 20:51 Комментировать

    Спасибо, все понятно написано, благодаря вашей статье быстро разобрался!

  • aave1
    aave1 18.12.2019 18:52 Комментировать

    Гриша, я рад, что смог вам помочь!

  • Никита
    Никита 26.04.2020 16:31 Комментировать

    Доброго времени суток!
    Подскажите, инструкция подходит для работы с W25Q64 ?
    Пока в наличии нет (жду посылку), ищу инструкцию для работы с указанной flash памятью.
    Буду благодарен за помощь!

  • aave1
    aave1 26.04.2020 16:57 Комментировать

    Никита, доброго времени!
    Я не изучал подробно вашу ПЗУшку W25Q64, но с первого взгляда она очень похожа на описываемую здесь. По крайней мере, подключение аналогично (если берём в том же корпусе), а на 21-ой странице datasheet приведён набор инструкций SPI, в котором команды чтения, записи, разрешения записи и некоторые другие аналогичны. Надо, конечно, ознакомиться внимательнее, но в целом ничего сложного тут нет. Действуйте согласно документации разработчика. Соберите схему и начните изучать работу с микросхемой с чтения регистров. Хорошие кандидаты для начала - регистры Device ID (0x90), JEDEC ID (0x9F). Если "пощупаете" их, прочитаете значения, которые должны там быть согласно документации, то станет понятно, что вы на верном пути, и это придаст уверенности в дальнейшем изучении.

  • Никита
    Никита 29.04.2020 20:12 Комментировать

    Большое спасибо за ответ!

  • Сергей
    Сергей 19.11.2020 17:58 Комментировать

    Доброго времени суток! Прочитал и отработал ваши примеры в данной статье на микросхеме флэш памяти W25Q32FV. Спасибо за материал. Всё работает нормально. Начал экспериментировать с кодом.

    В вашем примере адрес передается тремя байтами ADDR1, ADDR2, ADDR3. Разобрался по даташиту со структурой памяти W25Q32FV но всё равно возникли вопросы.

    Как записать данные в другой адрес отличного от примера? То есть, как записать и как разбить, например адрес типа 8192 (0x2000h) начального байта сектора 2, на три байта и приписать их переменным ADDR1, ADDR2, ADDR3 ??? Пробовал по разному. У меня получилось только побитовой передачей в цикле for.

    ПРИМЕР:
    const byte ADDR[24] = {0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0}; //24-битный адрес 0x2000h в двоичном виде и в массиве
    for(byte b=0; b = 24; b++){SPI.transfer(ADDR[b]);} //предаём 24-битный адрес побитно, вставлял его вместо ADDR1....ADDR3

  • Сергей
    Сергей 19.11.2020 18:03 Комментировать

    И ещё вопрос не по теме:
    В ардуино к сожалению не работает оператор round с указанием количества знаков после запятой.
    То есть, выражение:
    float number = 1,674938;
    float VOLT = round(number,2); //округление до двух знаков после запятой.
    НЕ РАБОТАЕТ!!! Оператор "round" в ардуино умеет округлять только до целых чисел.

    Работает следующий вариант:
    float number = 1,674938;
    int NUM = round(number * 100); //умножаем на 100 и округляем до целых чисел. Получаем 167.
    float VOLT = float(NUM / 100); //167 делим на 100 и получаем 1.67

    Это я проверял. Это правда или я что-то не так делаю?

  • aave1
    aave1 21.11.2020 19:01 Комментировать

    Сергей, что касается записи ПЗУшек, то в двух словах, ADDR1...ADDR3 - это 24-разрядный адрес, который следует читать как одно большое число. Например, адрес 8192 будет записываться как 0x002000 - теми же 24-мя битами (3 байта). Судя по тому, что я вижу в даташите, в вашем случае адрес передаётся аналогично - 3-мя байтами. Адресное пространство ПЗУшки состоит из 64-х блоков по 64 кб, т.е. в каждом блоке адреса с 0x000000 по 0x00ffff.

  • aave1
    aave1 21.11.2020 19:02 Комментировать

    А что касается округления чисел... В штатной библиотеке ардуино функция round() умеет округлять только до целого числа, так и есть. Ваш вариант с умножением и потом делением рабочий. Можно ещё вот так сделать:

    double p = 3.14159265359;

    void setup()
    {
    Serial.begin(9600);
    Serial.println(p, 10); // выводим с точностью до 10 знаков
    Serial.println(roundTo(p, 2), 10); // округляем до 2-х знаков и выводим 10 после запятой
    Serial.println(roundTo(p, 3), 10); // округляем до 3-х и выводим 10
    Serial.println(roundTo(p, 4), 10); // округляем до 4-х и выводим 10
    }

    double roundTo(double value, int decimal)
    {
    double pwr = pow(10, decimal);
    return (round(value * pwr) / pwr);
    }

    Вывод данного скетча будет:
    3.1415927410
    3.1400003433
    3.1420006752
    3.1416006088
    Если обратите внимание, даже вывод числа с такой большой точностью в первой строчке уже некорректен. Микроконтроллер не очень дружит с точной математикой.

  • Аллайя
    Аллайя 15.08.2021 21:37 Комментировать

    Может кому пригодится.
    Подключение флешек на 1.8 вольта - https://disk.yandex.ru/i/TAQuHij2_kf1Fg

  • aave1
    aave1 16.08.2021 07:04 Комментировать

    Аллайя, спасибо.

  1. Arduino это...
  2. Arduino это...
  3. Arduino это...
Отличный способ начать знакомство с электроникой, микроконтроллерами и программированием!
Замечательное средство для создания собственных электронных устройств, которые пригодятся в быту или для развлечения!
Уникальный конструктор, для которого разработаны десятки совместимых датчиков и модулей!
next
prev