Русский
Русский
English
Статистика
Реклама

Eeprom

Сброс пробега для RICOH SP 150SUw

09.02.2021 20:10:35 | Автор: admin
Я не верю в теорию заговора производителей (электроники) о сокращении ресурса изделия, просто выполняется поиск оптимального соотношения цены / качества для определенного сегмента рынка. Но есть один момент, от которого у меня дико подгорает чуть ниже живота. Это производители принтеров. Мало того, что цены на расходники такие, что оправдать их может только наличие в составе крови младенца, рожденного девственницей, но и повсеместное чипирование картриджей, перешедшее уже в не самые бюджетные лазерные агрегаты. Но реально бомбануло меня от следующего изделия: RICOH SP 150SUw. Данный девайс был приобретен по причине переезда из Подмосковья в Минск и необходимостью копирования / печати большого количества документов для получения разрешения на работу / проживание. Выбор был основан на следующих требованиях: МФУ не дорогой, компактный, сетевая печать, наличие драйверов под Linux (Fedora / Mint / OpenSUSE). Герой статьи обладал всеми этими характеристиками, но один нюанс был не замечен. Кривой счетчик ресурса. Если интересно, как вылечить и
(Памятка для граммар-наци от человека с легкой степенью дисграфии)
Да, у меня есть проблемы с естественными языками. Я это осознаю. И предупреждаю / предполагаю, что в тексте допущено достаточно много орфографических и пунктуационных ошибок. Надеюсь, нет логических и технических. Это не желание возрождать олбанию, просто так получается. Не могу позволить личного корректора, а написание статей мне не приносит прибыли, только отнимает время, но хочется поделиться идеями/мыслями с сообществом. На ошибки указывать не надо, не буду исправлять, так как к моменту исправления, с последующим вычитыванием, статья уже уйдет в глубины ленты Хабра и работа будет напрасной. Покорнейше прошу отнестись с пониманием, а если Вас передергивает от неправильно поставленной запятой, лучше не открывайте пост. Пожалейте свои и мои нервы, иначе
добро пожаловать!

Дело в том,что МФУ считает не количество израсходованного тонера, а количество отпечатанных страниц. Если стартовый картридж я отпечатал с достаточно большой степенью заполнения, то со вторым случился казус. Дело в том, что жена достаточно быстро влилась в местное сообщество англомам и часть работы, касающаяся обеспечения полиграфией, легла на нас. Так вот, всякие там контурные картинки для вырезания / раскрашивания / оригами заполняют тонером, от силы, 1% листа. В результате у меня оказался пустой по счетчику, но фактически полный процентов на 70 картридж. Нет, я не настолько беден, чтоб не позволить себе купить новый, не на столько экологически воспитан, чтоб испытывать моральные мучения, выкидывая исправное изделие на свалку, но от чувства, что тебя законно развели кресло начало дымиться, что и было порывом к действию.

Честно, не надеялся стать мамкиным хакером, учитывая что подобную аппаратную защиту очень сложно сломать. Рассчитывал, как минимум, на приличные алгоритмы шифрования и OTP память (однократно программируемая). Но реальность оказалась куда банальней. К счастью, по запросу обнуления картриджа, было много инструкций, а защитным чипом оказалась вполне себе распространенная I2C EEPROM AT24C01, распаянная с минимальным обвесом не плату. По большому счету, на следующей картинке, можно закончить статью:

Оригинальное видео

Любым программатором читаем содержимое микросхемы, зануляем ячейки, обведенные красными рамками и изменяем пару последних цифр серийного номера. Следует обратить внимание, что серийник текстовая строка, заканчивающаяся пробелом, так что менять надо в диапазоне 0...9 (0x30...0x39). Физический адрес микросхемы, распаянной на плате 0x03. Но Встречайте сапожника без сапог. Нет универсального программатора, по этому берем PIC16F819 и PICKit 3, не, для продвижения в массы Arduino UNO / Nano, пару резисторов на 4.7k (от 3k до 10k для этой задачи сойдет), метр МГТФа или Вашего любимого провода и собираем следующую схему:


В сборе это смотрится так:


Плату защиты я снял с картриджа, чтобы удостовериться, что приведенная в мануале распиновка соответствует реальности и получения физического адреса микросхемы на шине:

Контактные площадки, слева направо: GND, +5V, SCL. SDA.

Можно не мудрить с изготовлением переходника, а подпаять провода непосредственно на плату, не снимая ее с картриджа. Далее копируем мой говнокод в среду Arduino IDE:
Говнокод
#include <stdint.h>#include <Wire.h>//----------------------------------------------------------------#define EERROM_SZ         (0x80)#define EERROM_PHY_ADDR   (0x03)#define EERROM_HEAD       (0x50)#define PRN_BUFF_SZ       (0x08)#define SER_START_ADDR    (0x20)#define SER_END_ADDR      (0x2B)#define SER_MOD_ADDR0     (0x2A)#define SER_MOD_ADDR1     (0x29)#define SER_MOD_ADDR2     (0x28)//----------------------------------------------------------------static uint8_t eeprom_data[EERROM_SZ];static bool erased;static bool z_filled;//----------------------------------------------------------------static uint8_t ee_read(uint8_t phy_addr, uint8_t addr){  uint8_t res;  Wire.beginTransmission(EERROM_HEAD | phy_addr);  Wire.write(addr);  Wire.endTransmission();  Wire.requestFrom(EERROM_HEAD | phy_addr, 1);  res = Wire.read();    return res;}//----------------------------------------------------------------static void ee_write(uint8_t phy_addr, uint8_t addr, uint8_t data){  Wire.beginTransmission(EERROM_HEAD | phy_addr);  Wire.write(addr);  Wire.write(data);  Wire.endTransmission();  delay(5);}//----------------------------------------------------------------static void read_data(uint8_t phy_addr){  uint8_t addr;  uint8_t data;    erased = true;  z_filled = true;    Serial.print("Read from phy addr ");  Serial.print(phy_addr);  for (addr = 0; addr < EERROM_SZ; addr++)  {    if (0 == (addr & 0x03))    {      Serial.print(".");    }    data = ee_read(phy_addr, addr);    eeprom_data[addr] = data;    if (0xFF != data)    {      erased = false;    }    if (0x00 != data)    {      z_filled = false;    }  }    Serial.println("Ok");}//----------------------------------------------------------------static void write_data(uint8_t phy_addr){  uint8_t addr;  Serial.print("Write to phy addr ");  Serial.print(phy_addr);  for (addr = 0; addr < EERROM_SZ; addr++)  {    if (0 == (addr & 0x03))    {      Serial.print(".");    }    ee_write(phy_addr, addr, eeprom_data[addr]);  }  Serial.println("Ok");}//----------------------------------------------------------------static bool check_data(uint8_t phy_addr){  uint8_t addr;  uint8_t data;    Serial.print("Check from phy addr ");  Serial.print(phy_addr);  for (addr = 0; addr < EERROM_SZ; addr++)  {    if (0 == (addr & 0x03))    {      Serial.print(".");    }    data = ee_read(phy_addr, addr);    if (eeprom_data[addr] != data)    {      Serial.println("FAILED");      return false;    }  }    Serial.println("Ok");  return true;}//----------------------------------------------------------------static void print_data(void){  uint16_t addr;  char prn_buff[PRN_BUFF_SZ];    for(addr = 0; addr < EERROM_SZ; addr++)  {    if (0x00 == (addr & 0x0F))    {      snprintf(prn_buff, PRN_BUFF_SZ, "%4X:  ", addr);      Serial.print(prn_buff);    }        snprintf(prn_buff, PRN_BUFF_SZ, "%2X ", eeprom_data[addr]);    Serial.print(prn_buff);        if (0x0F == (addr & 0x0F))    {      Serial.print("\n\r");    }  }  Serial.print("\n\r");}//----------------------------------------------------------------static void prn_serial(void){  Serial.print("Serial #: ");  Serial.write(&eeprom_data[SER_START_ADDR], 1 + SER_END_ADDR - SER_START_ADDR);  Serial.print("\n\r");}//----------------------------------------------------------------static void mod_serial(void){  eeprom_data[SER_MOD_ADDR0]++;  if (eeprom_data[SER_MOD_ADDR0] > '9')  {    eeprom_data[SER_MOD_ADDR0] = '2';  }  eeprom_data[SER_MOD_ADDR1]++;  if (eeprom_data[SER_MOD_ADDR1] > '9')  {    eeprom_data[SER_MOD_ADDR1] = '3';    eeprom_data[SER_MOD_ADDR2]++;    if (eeprom_data[SER_MOD_ADDR2] > '9')    {      eeprom_data[SER_MOD_ADDR2] = '1';    }  }}//----------------------------------------------------------------static void reset_mileage(void){  uint8_t i;    for (i = 0x12; i <= 0x1F; i++)  {    eeprom_data[i] = 0;  }  for (i = 0x2C; i <= 0x7F; i++)  {    eeprom_data[i] = 0;  }}//----------------------------------------------------------------static bool test_magic(void){  if (0x32 != eeprom_data[0]) return false;  if (0x00 != eeprom_data[1]) return false;  if (0x01 != eeprom_data[2]) return false;  if (0x03 != eeprom_data[3]) return false;  return true;}//----------------------------------------------------------------void setup(){  int key;  Serial.begin(9600);  Wire.begin();  Serial.println("\tSP 150 cartridge mileage resetter");  Serial.println("Connect like this:");  Serial.println("             TOP");  Serial.println("______________________________");  Serial.println("|o |GND| |+5V| |SCL| |SDA|  <=");  Serial.println("|  |GND| | 5V| | A5| | A4|    ");  Serial.println("------------------------------");  Serial.println("        cartridge roller");    Serial.println("\n\r\n\r\tTo start, press 'm' or any button for test (not prog)...\n\r");  do  {    key = Serial.read();  }  while(-1 == key);  #if 0  for (uint8_t paddr = 0; paddr < 8; paddr++)  {    Serial.print("Scan phy ");    Serial.println(paddr);    for (uint8_t i = 0; i < 5; i++)    {      Serial.print("Read from ");      Serial.print(i);      Serial.print(".........");      Serial.println(ee_read(paddr, i));    }  }  return;#endif  read_data(EERROM_PHY_ADDR);  Serial.println("Read:");  print_data();      if (true == erased)  {    Serial.println("ERROR! The EEPROM is erased or the connection / phy addr is incorrect.");    return;  }  if (true == z_filled)  {    Serial.println("ERROR! The EEPROM is Z filled.");    return;  }    if (false == test_magic())  {    Serial.println("ERROR! Invalid magic number.");    return;  }  prn_serial();    mod_serial();  reset_mileage();  Serial.println("\n\rModified:");  print_data();  prn_serial();  if ('m' != (char)key)  {    Serial.println("WARNING! The data was not modified in the EEPROM");    return;  }  write_data(EERROM_PHY_ADDR);  if (false == check_data(EERROM_PHY_ADDR))  {    return;  }    Serial.println("Fin");}void loop(){  //do nothing}


(Про говнокод да, не оптимизировал ни по памяти (читаются все данные в массив), ни по производительности (чтение и запись выполняется побайтно), ни по функционалу, но для такой простой задачи и так сойдет!)

Прошиваем Ардуинку, открываем любой терминал (I18n, 9600бод), подойдет встроенный в Arduino IDE, сбрасываем платку, жмем любую кнопку:


После этого содержимое EEPROM будет прочитано, модифицировано, но не записано. Если процедура произошла без ошибок снова сбрасываем плату, нажимаем m, после чего выполнятся все этапы и модифицированные данные будут записаны. Если что пошло не так, повторно сверьтесь со схемой и повторите попытку. После удачного обнуления отпаиваем провода и устанавливаем картридж на место. Уровень тонера должен быть 100%.

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

Работа с параметрами в EEPROM

02.06.2021 22:14:13 | Автор: admin

Введение

Привет Хабр. Наконец-то у меня появилось свободное время и я могу еще немного поделиться своим опытом, возможно кому-то это будет полезно, и поможет в его работе, и я этому буду безусловно рад. Ну что же,....

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

Всякий раз, когда студент решает что-то сохранить в EEPROM, это выливается во множество багов, связанных как с неверно выбранной архитектурой, так и просто человеческим фактором. Собственно обычно студент лезет в интернет и находит что-то типа этого:

int address = 0;float val1 = 123.456f;byte val2 = 64;char name[10] = "Arduino";EEPROM.put(address, val1);address += sizeof(val1); //+4EEPROM.put(address, val2);address += sizeof(val2); //+1EEPROM.put(address, name);address += sizeof(name); //+10

Этот замечательный код лапшой разрастается по всему проекту, применяясь к месту и не совсем в каждом из 100 EEPROM параметров, имеющих разный тип, длину и адрес. Немудрено, что где-то да и допустит торопливый студент ошибку.

Кроме того, обычно студенты используют РТОС, а потому нужно понимать, что обращение к EEPROM из разных потоков может привести либо к фейлам, либо ко всяким там дедлокам. Поэтому если студент использует EEPROM, я вначале прошу нарисовать дизайн, чтобы показать как он собирается работать этой подсистемой.

Обычно все сводится к двум вариантам:

  • Доступ к EEPROM только из одного места. Типа такой EepromManager , который запускается в отдельной задаче и проходится по списку кешеруемых EEPROM параметров и смотрит, было ли в них изменение, и если да, то пишет его в EEPROM.

    Тут очень большой и толстый плюс: Не нужно блокировать работу с EEPROM, все делается в одном месте.

    Но есть и минусы: обычно запись таких параметров происходит, когда пользователь что-то послал через внешний интерфейс, скажем Modbus протокол, ну типа он говорит, что я тут записал новые единицы измерения - Ок отвечает ему программа, я их закешировала, но на самом деле запись еще не прошла. Она пройдет позже, а ответить пользователю нужно прямо сейчас. И вот пользователь получает ответ, что все хорошо, новые единицы установлены, но вдруг во время отложенной записи происходит сбой и реальные единицы на самом деле не записались. В итоге устройство как бы обмануло пользователя. Понятно, что оно должно выставить ошибку и как-то об этом сообщить в своем статусе, но все же, пользователь уже немного с недоверием начинает смотреть на ваше устройство.

  • Второй способ - пишем всегда сразу по месту.

    Плюс в том, что пользователь всегда получает достоверный ответ. Мы не задумываясь пишем параметр в EEPROM там где надо, и это выглядит просто.

    Но проблем от этого не меньше: так как мы можем писать хоть что, хоть откуда, хоть куда - скажем журнал ошибок из разных подсистем из разных задач, то придется задуматься о блокировке ресурса EEPROM.

    Кроме того возможно проблема с быстрыми протоколами, когда ответить нам нужно в течении ограниченного времени, скажем 5 мс, а те кто работал с EEPROM знают, что записывается там все постранично. Ну точнее, чтобы записать однобайтовый параметр, EEPROM, копирует целую страницу во свой буфер, меняет в этом буфере этот один несчастный байт, стирает страницу, и затем записывает буфер (ну т.е. всю страницу) и того на запись одной страницы сразу тратится от 5 до 10 мс, в зависимости от размера страницы.

Но в обоих этих способах, мы хотим, чтобы доступ к параметрам не был похож, на тот код с Ардуино, что я привел, а был простым и понятным, в идеале, чтобы было вообще так:

//Записываем 10.0F в EEPROM по адресу, где лежит myEEPROMData параметр myEEPROMData = 10.0F;

Но так мы делать не будем, потому что иногда нам понадобится по месту вернуть статус операции записи, вдруг EEPROM битая или проводки отпаялись. И посему мы будем делать, что-то похожее на это:

//Записываем в EEPROM строку из 5 символов по адресу параметра myStrDataauto returnStatus = myStrData.Set(tStr6{"Hello"}); if (!returnStatus){std::cout << "Ok"}//Записываем в EEPROM float параметр по адресу параметра myFloatDatareturnStatus = myFloatData.Set(37.2F); 

Ну что же приступим

Анализ требований и дизайн

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

Давайте поймем, что мы вообще хотим. Сформируем требования более детально:

  • Каждая наша переменная(параметр) должна иметь уникальный адрес в EEPROM

    • Мы не хотим руками задавать этот адрес, он должен высчитываться сам, на этапе компиляции, потому что мы не хотим, чтобы студент нечаянно задал неверный адрес и сбил все настройки

  • Мы не хотим постоянно лазить в EEPROM, когда пользователь хочет прочитать параметр

    • Обычно EEPROM подключается через I2C или SPI. Передача данных по этим интерфейсам тоже отнимает время, поэтому лучше кэшировать параметры в ОЗУ, и возвращать сразу копию из кеша.

  • При инициализации параметра, если не удалось прочитать данные с EEPROM, мы должны вернуть какое-то значение по умолчанию.

  • На плате могут быть несколько EEPROM, а может вообще и не EEPROM, а скажем второй процессор, где хранятся разные данные, поэтому мы должны предусмотреть, возможность того, чтобы параметр мог использовать заданный драйвер для записи и чтения в нужное место.

  • Все должно быть дружелюбным простым и понятным :)

Давайте прикинем дизайн класса, который будет описывать такой параметр и удовлетворять нашим требованиям: Назовем класс CaсhedNvData

CachedNvData

Вообще все должно быть понятно из картинки, но на всякий случай:

При вызове метода Init() мы должны полезть в EEPROM и считать оттуда нужный параметр с нужного адреса.

Адрес будет высчитываться на этапе компиляции, пока эту магию пропустим. Прочитанное значение хранится в data, и как только кому-то понадобится, оно возвращается немедленно из копии в ОЗУ с помощью метода Get().

А при записи, мы уже будем работать с EEPROM через nvDriver. Можно подсунуть любой nvDriver, главное, чтобы у него были методы Set() и Get(). Вот например, такой драйвер подойдет.

NvDriver

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

Например, если у нас есть 3 параметра:

//Длина параметра 6 байтconstexpr CachedNvData<NvVarList, tString6, myStrDefaultValue,  nvDriver> myStrData;//Длина параметра 4 байтаconstexpr CachedNvData<NvVarList, float, myFloatDataDefaultValue, nvDriver> myFloatData;//Длина параметра 4 байтconstexpr CachedNvData<NvVarList, std::uint32_t, myUint32DefaultValue,  nvDriver> myUint32Data; 

То когда мы сделаем какой-то такой список:

NvVarList<100U, myStrData, myFloatData, myUint32Data>

У нас бы у myStrData был бы адрес 100, у myFloatData - 106, а у myUint32Data - 110. Ну и соответственно список мог бы его вернуть для каждого из параметра.

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

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

Сделаем такой базовый класс, назовем его NvVarListBase:

NvVarListBase

В прицнипе то и все.

Код

А теперь самая простая часть - пишем код. Комментировать не буду, вроде бы и так понятно

CaсhedNvData

template<typename NvList, typename T, const T& defaultValue, const auto& nvDriver>class CaсhedNvData{  public:    ReturnCode Set(T value) const    {      //Ищем адрес EEPROM параметра в списке       constexpr auto address =                 NvList::template GetAddress<NvList,T,defaultValue,nvDriver>();      //Записываем новое значение в EEPROM      ReturnCode returnCode = nvDriver.Set(                                address,                                reinterpret_cast<const tNvData*>(&value), sizeof(T));      //Если значение записалось успешно, обновляем копию в ОЗУ      if (!returnCode)      {        memcpy((void*)&data, (void*)&value, sizeof(T));      }      return returnCode;    }    ReturnCode Init() const    {      constexpr auto address =                 NvList::template GetAddress<NvList,T,defaultValue,nvDriver>();      //Читаем значение из EEPROM      ReturnCode returnCode = nvDriver.Get(                                address,                                 reinterpret_cast<tNvData*>(&data), sizeof(T));      //Tесли значение не прочиталось из EEPROM, устанавливаем значение по умолчанию      if (returnCode)      {        data = defaultValue;      }      return returnCode;    }    T Get() const    {      return data;    }        using Type = T;  private:    inline static T data = defaultValue;};
template<const tNvAddress startAddress, const auto& ...nvVars>struct NvVarListBase{        template<typename NvList, typename T, const T& defaultValue, const auto& nvDriver>    constexpr static size_t GetAddress()    {       //Ищем EEPROM адрес параметра с типом       //CaсhedNvData<NvList, T, defaultValue, nvDriver>      using tQueriedType = CaсhedNvData<NvList, T, defaultValue, nvDriver>;                  return startAddress +             GetAddressOffset<tQueriedType>(NvVarListBase<startAddress,nvVars...>());    }      private:       template <typename QueriedType, const auto& arg, const auto&... args>       constexpr static size_t GetAddressOffset(NvVarListBase<startAddress, arg, args...>)   {    //Чтобы узнать тип первого аргумента в списке,     //создаем объект такого же типа как и первый аргумент    auto test = arg;    //если тип созданного объекта такой же как и искомый, то заканчиваем итерации    if constexpr (std::is_same<decltype(test), QueriedType>::value)    {        return  0U;    } else    {      //Иначе увеличиваем адрес на размер типа параметра и переходим к       //следующему параметру в списке.        return sizeof(typename decltype(test)::Type) +                 GetAddressOffset<QueriedType>(NvVarListBase<startAddress, args...>());    }  }    };

Использование

А теперь встанем не место студента и попробуем это все дело использовать.

Задаем начальные значения параметров:

using tString6 = std::array<char, 6U>;inline constexpr float myFloatDataDefaultValue = 10.0f;inline constexpr tString6 myStrDefaultValue = {"Habr "};inline constexpr std::uint32_t myUint32DefaultValue = 0x30313233;

Зададем сами параметры:

//поскольку список ссылается на параметры, а параметры на список. //Используем forward declarationstruct NvVarList;   constexpr NvDriver nvDriver;//Теперь можем использовать NvVarList в шаблоне EEPROM параметровconstexpr CaсhedNvData<NvVarList, float, myFloatDataDefaultValue, nvDriver> myFloatData;constexpr CaсhedNvData<NvVarList, tString6, myStrDefaultValue,  nvDriver> myStrData;constexpr CaсhedNvData<NvVarList, uint32_t, myUint32DefaultValue,  nvDriver> myUint32Data;

Теперь осталось определить сам список параметров. Важно, чтобы все EEPROM параметры были разных типов. Можно в принципе вставить статическую проверку на это в NvVarListBase, но не будем.

struct NvVarList : public NvVarListBase<0, myStrData, myFloatData, myUint32Data>{};

А теперь можем использовать наши параметры хоть где, очень просто и элементарно:

struct NvVarList;constexpr NvDriver nvDriver;using tString6 = std::array<char, 6U>;inline constexpr float myFloatDataDefaultValue = 10.0f;inline constexpr tString6 myStrDefaultValue = {"Habr "};inline constexpr uint32_t myUint32DefaultValue = 0x30313233;constexpr CaсhedNvData<NvVarList, float, myFloatDataDefaultValue, nvDriver> myFloatData;constexpr CaсhedNvData<NvVarList, tString6, myStrDefaultValue,  nvDriver> myStrData;constexpr CaсhedNvData<NvVarList, uint32_t, myUint32DefaultValue,  nvDriver> myUint32Data;struct NvVarList : public NvVarListBase<0, myStrData, myFloatData, myUint32Data>{};int main(){        myStrData.Init();    myFloatData.Init();    myUint32Data.Init()        myStrData.Get();    returnCode = myStrData.Set(tString6{"Hello"});    if (!returnCode)    {        std::cout << "Hello has been written" << std::endl;    }    myStrData.Get();    myFloatData.Set(37.2F);        myUint32Data.Set(0x30313233);        return 1;}

Можно передавать ссылку на них в любой класс, через конструктор или шаблон.

template<const auto& param>struct SuperSubsystem{  void SomeMethod()  {    std::cout << "SuperSubsystem read param" << param.Get() << std::endl;   }};int main(){    SuperSubsystem<myFloatData> superSystem;  superSystem.SomeMethod();}

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

Ссылка на пример кода тут: https://godbolt.org/z/W5fPjh6ae

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

Подробнее..

Вспомнить всё. Разбираемся в полупроводниковой памяти

29.12.2020 18:11:47 | Автор: admin

Когда я писал в начале года статью Кто есть кто в мировой микроэлектронике, меня удивило, что в десятке самых больших полупроводниковых компаний пять занимаются производством памяти, в том числе две только производством памяти. Общий объем мирового рынка полупроводниковой памяти оценивается в 110 миллиардов долларов и является постоянной головной болью участников и инвесторов, потому что, несмотря на долгосрочный рост вместе со всей индустрией микроэлектроники, локально рынок памяти очень сильно лихорадит 130 миллиардов в 2017 году, 163 в 2018, 110 в 2019 и 110 же ожидается по итогам 2020 года.

Топ-10 мировых микроэлектронных компаний, производители памяти выделены красным.Топ-10 мировых микроэлектронных компаний, производители памяти выделены красным.

Объем рынка памяти близок к трети всей микроэлектроники, а в десятке самых больших компаний памятью занимается половина. Так чем же полупроводниковая память такая особенная? Давайте разбираться.

Особую важность памяти придает то, что ее всегда нужно много. Я бы даже сказал, что ее всегда нужно больше, чем есть. Билл Гейтс, которого вы наверняка сейчас вспомнили, на самом деле никогда ничего не говорил про 640 Кб, примерно как Мария-Антуанетта ничего не говорила про пирожные. Впрочем, в начале восьмидесятых 640 КБ были огромной цифрой. И что с того, что памяти нужно много? спросите вы. Очень просто большие тиражи позволяют разработчикам концентрироваться на одном продукте и оптимизировать не только дизайн, но и технологию производства. Сейчас в большинстве случаев чипы памяти производятся на фабриках, специально предназначенных для чипов памяти и принадлежащих производителям памяти. Это принципиальное отличие от всех остальных типов микросхем, где пути разработчиков и производителей давным-давно разошлись, и бал правят контрактные фабрики типа TSMC.

Начнем, собственно, с определения и классификации. Точнее, с классификаций, потому что типов памяти очень много, и различных применений тоже. Классическое разделение памяти по применению на кэш-память, оперативную память и память хранения данных. Оно же примерно соответствует делению на статическую (SRAM), динамическую память (DRAM) и диски (HDD и SSD).

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

Диаграмма иерархии памяти в вычислительных системах, с относительными объемами памяти и задержками обращения.Диаграмма иерархии памяти в вычислительных системах, с относительными объемами памяти и задержками обращения.

Кэш-память

Самый первый уровень памяти в вычислительной системе это регистровый файл и кэш-память. Для них определяющее значение имеет скорость доступа, а вот объем может быть небольшим, особенно если его вдумчиво наполнять. Кэш обычно делается на основе статической памяти. Ячейка статической памяти может быть выполнена по-разному, но обязательно содержит в себе положительную обратную связь, которая позволяет хранить информацию и не терять ее (в отличие от динамической памяти, которой требуется периодическая перезапись). В КМОП-технологии ячейка статической памяти состоит из четырех транзисторов собственно запоминающего элемента и одного и более транзисторов, обеспечивающих чтение и запись информации. Промышленный стандарт так называемая 6T-ячейка.

Электрическая схема 6T-ячейки SRAMЭлектрическая схема 6T-ячейки SRAM

Шесть транзисторов это очень много, особенно в сравнении с DRAM или флэш-памятью, где для хранения одного бита информации требуется два, а то и всего один элемент. Тем не менее, скорость работы сделала свое дело, и в большинстве современных цифровых микросхем статическая память занимает десятки процентов площади. Этот факт, кстати, сделал ячейку SRAM точкой опоры в определении проектных норм производства чипов: когда маркетинговые цифры те самые пресловутые 28, 7 или 5 нм отвязались от физических размеров элементов на кристалле, улучшение плотности упаковки стали считать как соотношение площади ячейки SRAM на старом и новом техпроцессах. Если в новой технологии ячейка стала в два раза меньше, значит проектные нормы уменьшились в корень из двух раз.

Разные варианты топологии шеститранзисторной ячейки статической памяти. Источник G. Apostolidis et. al., Design and Simulation of 6T SRAM Cell Architectures in 32nm Technology, Journal of Engineering Science and Technology Review, 2016Разные варианты топологии шеститранзисторной ячейки статической памяти. Источник G. Apostolidis et. al., Design and Simulation of 6T SRAM Cell Architectures in 32nm Technology, Journal of Engineering Science and Technology Review, 2016

Отдельные чипы SRAM были популярны в составе многокристалльных микропроцессоров, таких как девайсы, построенные на базе серии Am2900 или советской 581 серии. При этом, как только появилась возможность поместить достаточно транзисторов на один чип, кэш-память стали размещать на том же кристалле, что и вычислитель, чтобы сэкономить мощность и увеличив скорость работы, избавившись от медленных и громоздких соединений между чипами. В современных микропроцессорах на одном кристалле помещается многоуровневый набор блоков кэш-памяти объемом в несколько Мегабайт. Это, кстати, привело к тому, что рынок SRAM как отдельного продукта практически перестал существовать: его объем оценивается всего в 420 миллионов долларов, то есть в 0.3% от всего рынка полупроводниковой памяти, и продолжает сокращаться. Последние из остающихся могикан чипы для тяжелых условий эксплуатации, вроде космоса, высокотемпературных промышленных установок или медицинской техники, где нельзя свободно применять обычные коммерческие микросхемы и где из-за этого микроэлектронный прогресс несколько отстает. Есть некоторые перспективы роста в автомобильной электронике и в интернете вещей, где для постоянно включенных устройств не играет роли главный недостаток SRAM неспособность хранить информацию после отключения питания. Только хранить, а не обрабатывать SRAM может с минимальным энергопотреблением, так что это может быть интересным вариантов. Впрочем, в этой конкретной нише, кроме флэш-памяти, есть еще активно развивающиеся новые виды памяти, такие как MRAM, так что перспективы на самом деле весьма туманны, а производители один за одним уходят из стагнирующего сегмента, что позволило Cypress получить больше половины рынка повторюсь, крошечного по мировым меркам.

Оперативная память и динамическая память

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

Схемы ячейки динамической и статической памятиСхемы ячейки динамической и статической памяти

Намного более простая ячейка позволяет существенно увеличить количество памяти на кристалле. Уже самый первый серийный кристалл DRAM (Intel 1103) в 1970 году содержал 1024 бита, а современные чипы умещают уже 16 Гигабит! Это стало возможным благодаря постоянному прогрессу технологии производства, а именно разнообразным улучшениям конструкции интегрального конденсатора. Если в самых первых чипах использовалась просто МОП-емкость, крайне похожая по конструкции на транзистор, в современных чипах DRAM конденсатор для экономии площади располагается не горизонтально, а вертикально, под или над транзистором.

Условная схема прогресса технологии производства DRAM.Условная схема прогресса технологии производства DRAM.

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

Небольшой исторический экскурс, про Intel

Компания Intel была основана в 1968 году с прицелом на рынок памяти. Основатели фирмы считали, что относительно новые тогда интегральные схемы имеют потенциал вытеснить с рынка вычислительных машин память на магнитных сердечниках. Первыми продуктами Intel были чипы биполярной статической памяти, почти сразу же за ними последовала серия крайне успешных чипов DRAM, а вот заказы на разработку микропроцессоров очень долго рассматривались как что-то временное и побочное до середины восьмидесятых, когда серьезная конкуренция со стороны японских производителей DRAM, таких как Toshiba, вынудила компанию уйти с рынка памяти.

Позднее, Intel развивал бизнес по производству флэш-памяти, совместно с другой американской компанией, Micron, но буквально на днях продал эти активы ей же, и в ближайшем будущем под маркой Intel будет выпускаться только память Optane, основанная на фазовых эффектах.

Общий объем мирового рынка DRAM оценивается в 60-80 миллиардов долларов и составляет чуть больше половины мирового рынка памяти. Оставшуюся часть почти целиком занимает NAND Flash, а на долю всего остального разнообразия приходится не более трех процентов рынка. Производство чипов DRAM держится на трех китах корейских Samsung и SK Hynix, а также американской компании Micron. Все три в пятерке крупнейших полупроводниковых компаний мира, причем если Samsung чем только не занимается, то Micron и SK Hynix производят только памяти, DRAM и Flash. Три гиганта занимают без малого 95% рынка, а остатки рынка почти полностью разделены между несколькими тайваньскими компаниями.

Основные рыночные ниши это потребительская электроника, включая смартфоны (40-50%), а также персональные компьютеры (15-20% ), серверное и телекоммуникационное оборудование (20-25%). Самые большие перспективы роста при этом ожидаются в автомобильном секторе, благодаря разного рода автопилотам и другим системам помощи водителю, а также в вычислениях, связанных с искусственным интеллектом.

Стоит отметить, что все чаще речь идет не о привычных нам планках памяти, а об аккуратной интеграции чипов на плату телефона или даже непосредственно в корпус процессора, в виде так называемой HBM high bandwidth memory. Такая конфигурация позволяет увеличить пропускную способность памяти за счет использования многоразрядных шин, которые нет возможности реализовать при соединении корпусов на печатной плате, уменьшить задержки и потребление, а также эффективно разместить кристаллы памяти в несколько слоев, разместить большую емкость на меньшей площади.

Внутренности корпуса графического ускорителя AMD Fiji. Центральный кристалл собственно вычислитель, по обеим сторонам упакованные в несколько слоев чипы HBM DRAM.Внутренности корпуса графического ускорителя AMD Fiji. Центральный кристалл собственно вычислитель, по обеим сторонам упакованные в несколько слоев чипы HBM DRAM.

Впрочем, и планки памяти тоже никуда не денутся в обозримом будущем, и спрос на них стабильно растет стараниями не только геймеров, но и производителей серверов. Объем рынка модулей памяти составляет приблизительно 16 миллиардов долларов, и он выглядит как Гулливер в окружении лилипутов рыночная доля Kingston Technology превышает 80%, против 2-3% у ближайших конкурентов. При этом сами чипы Kingston закупают у двух из трех больших производителей Micron и SK Hynix. Samsung не привлекается, видимо, в силу того, что большинство их чипов DRAM предназначено для мобильных телефонов.

Еще один небольшой исторический экскурс, про Kingston

Kingston американская компания, основанная в 1987 году, стала одним из пионеров внедрения SIMM-модулей как удобной альтернативы прямому поверхностному монтажу микросхем памяти. Быстро развиваясь на фоне роста рынка персональных компьютеров, Kingston стали единорогом с миллиардной капитализацией уже к 1995 году, и с тех пор выросли еще на порядок, увеличив долю на рынке модулей DRAM c 25% до 80% и расширившись на производство других продуктов, таких как SSD, где Kingston тоже является мировым лидером, правда с более скромными 26% мирового рынка против 8% и 6% у ближайших конкурентов.

Модуль оперативной памяти Kingston. Обратите внимание на плотность упаковки чипов на плате.Модуль оперативной памяти Kingston. Обратите внимание на плотность упаковки чипов на плате.

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

А что же японцы, правившие бал в восьмидесятых и вытеснившие с рынка DRAM Intel? В 1999 году профильные подразделения Hitachi и NEC объединились в компанию Elpida, которая позже поглотила DRAM-бизнес Mitshibishi. В двухтысячных компания активно развивалась, много вкладывала в перспективные производства и была поставщиком, например, для Apple.Но финансовый кризис 2009 года очень сильно подкосил Elpida, и в 2012 году она была вынуждена подать на банкротство, после чего была куплена Micron.

На этой печальной ноте давайте заканчивать с DRAM и переходить к flash-памяти, где все еще есть по крайней мере одна успешная японская компания.

Флэш-память и системы хранения данных

Главный недостаток как SRAM, так и DRAM то, что информация в них пропадает в случае, если им отключить питание. Но, сами понимаете, никогда не отключать питание довольно затруднительно, поэтому всю историю вычислительной техники использовались какие-нибудь системы для постоянного хранения данных начиная от перфокарт. Большую часть времени в качестве систем постоянного хранения данных использовались магнитные носители лента или жесткий диск. Жесткие диски сложные электромеханические системы, которые прошли огромный путь от первого образца IBM размером с небольшой холодильник, до 2.5-дюймовых HDD для ноутбуков. Тем не менее, прогресс в микроэлектронной технологии был быстрее, и сейчас мы с вами наблюдаем процесс практически полного замещения жестких дисков полупроводниковыми SSD. Последним годом денежного роста для рынка HDD был 2012, и сейчас он составляет уже не более трети от рынка флэш-памяти.

Разные поколения жестких дисков.Разные поколения жестких дисков.

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

Структура ячейки флэш-памятиСтруктура ячейки флэш-памяти

Чтение из флэш-памяти происходит следующим образом: на сток транзистора подается напряжение, после чего измеряется ток через транзистор. Если ток есть, значит на одном из двух затворов есть напряжение, если тока нет на обоих затворах ноль.

На практике структура чипов флэш-памяти несколько сложнее, потому что, кроме самого транзистора, есть еще металлизация управляющих линий, и инженерам пришлось пойти на некоторые ухищрения, чтобы уменьшить ее площадь. Изначально типов флэш-памяти было два NOR Flash и NAND Flash, различающихся как раз способом доступа к ячейкам. Названы они так по подобию соединения ячеек с соответствующими логическими элементами в NAND последовательно, в NOR параллельно.

Сравнение архитектур NOR Flash и NAND FlashСравнение архитектур NOR Flash и NAND Flash

Чтение из NOR Flash происходит ровно так, как описано выше, и позволяет удобно добраться до любого интересующего нас куска памяти. Чтение из NAND Flash несколько более занятно: для того, чтобы узнать значение интересующего нас бита в последовательно включенном стеке, нужно открыть управляющие затворы всех остальных транзисторов тогда на состояние выхода будет влиять только интересующий нас бит. Согласитесь, заряжать-разряжать множество управляющих затворов ради того, чтобы узнать значение только одного бита это как-то чересчур расточительно? Особенно с учетом того, что мы должны открыть управляющие затворы всех транзисторов не только в интересующем нас стеке, но и во всех соседних стеках, подключенных к тем же word line. Именно поэтому на практике NAND Flash читается не побитно, а целыми страницами. Это может показаться неудобным, ведь мы, по сути, делаем нашу память не совсем random-access.

Любые рассуждения на тему того, что лучше NAND Flash или NOR Flash, неизбежно натыкаются на мнение рынка, сделавшего однозначный выбор: объем рынка NAND 40-60 миллиардов долларов, а NOR около четырех. Почему же побеждает менее удобная память? Дело на самом деле не в удобстве или неудобстве, а в целевых приложениях и в стоимости. NOR Flash удобнее и быстрее читается, но очень медленно записывается, зато ячейка NAND Flash в два с лишним раза меньше, что, разумеется, критично в ситуации, когда вам нужно БОЛЬШЕ ПАМЯТИ.

Кроме того, если немного подумать над главным недостатком NAND Flash чтением только большими кусками, то в обычной вычислительной системе чтение из долгосрочной памяти в любом случае происходит большими кусками чтобы оптимизировать работу кэш-памяти и минимизировать число кэш-промахов. То есть этот недостаток на самом деле и не недостаток вовсе. Так что по факту единственное настоящее преимущество NOR Flash быстрота чтения, и ее основные применения так раз те, где требуется быстрое чтение, но не нужна частая и быстрая запись. Например, прошивки разнообразных embedded девайсов, где NOR Flash активно заменяет другие виды EEPROM.

Небольшое отступление: PROM

И, раз уж я упомянул EEPROM, нелишне обсудить и экстремальный случай когда память только читается, но не перезаписывается то есть Read-Only Memory или ROM. Такая память гораздо чаще используется в промышленных применениях и для разнообразных прошивок. Такая память может быть запрограммирована на этапе производства с помощью наличия или отсутствия металлических соединений (или транзисторов, как это было сделано в Intel 8086. Но что, если раз-другой записать память все-таки нужно, причем уже после того, как чип произведен? На этот случай существует довольно много разновидностей PROM (P programmable), довольно часто встраиваемых на кристалл вычислительной системы, например, микроконтроллера, но продолжающих активно использоваться и в качестве отдельных чипов.

Самый простой вариант это однократно программируемая память типа Antifuse, она же память на пережигаемых перемычках. Идея очень проста: у нас есть структура (транзистор или резистор), которая может быть необратимо разрушена, превратившись в короткое замыкание или разрыв цепи. Чтение такой памяти выглядит как проверка на наличие замыкания или разрыва, а запись возможна только один раз, потому что изменение структуры необратимо.

Внешний вид памяти на пережигаемых перемычкахВнешний вид памяти на пережигаемых перемычках

В случае, если может быть нужно записывать память несколько раз, например изредка обновлять прошивку, в дело вступают разные варианты EPROM (E erasable) и EEPROM (EE electrically erasable). Технологически они базируются на транзисторах с плавающим затвором и являются примитивной разновидностью флэш-памяти. Сейчас под термином EEPROM обычно подразумевают NOR Flash c возможностью побайтной записи и удаления данных.

NAND Flash

Что же касается NAND Flash, то ее стоимость за бит уже давно снизилась настолько, что этот вид памяти стремительно завоевывает рынок памяти для хранения информации, один за одним забивая гвозди в крышку гроба HDD и, например, дав на возможность иметь много памяти в крошечных мобильных телефонах. Ключевые производители чипов NAND Flash Samsung (33% и почти половина накопителей для телефонов), Kioxia (бывшая Toshiba, 20% рынка), Western Digital (14%), SK Hynix (11%), Micron (10%), Intel (8%).

Из этого списка, впрочем, надо исключить Intel, которые недавно объявили о переходе своей доли в совместном с Micron производстве к последним и об уходе с рынка флэш-памяти. Еще один интересный игрок Western Digital, один из гигантов HDD, ныне стремительно переориентирующийся на твердотельные диски и ставший для этго уникальным зверем fabless-производителем памяти. WD выкупили для этого больше трети производственных мощностей Kioxia, которые делают одни и те же чипы для себя и для клиента. Еще одно неожиданное последствие переориентации WD они стали одним из наиболее заметных участников коммьюнити RISC-V, активно внедряя эту систему команд в свои контроллеры накопителей.

И в завершение рассказа про NAND Flash, надо непременно рассказать о произошедшей в последние годы технологической революции. Флэш-память, как и обычная микроэлектроника, уже уперлась в технологический предел миниатюризации транзисторов, и если в вычислительных системах можно хотя попробовать отыграть что-то за счет архитектуры, то в памяти плотность упаковки это главное и единственное, что по-настоящему волнует. Поэтому, пока разговоры о переходе обычных КМОП-микросхем в третье измерение все еще остаются разговорами, 3D NAND уже четыре года как массово присутствует на рынке, позволяя разместить на кристалле в десятки, а то и в сотни раз больше ячеек памяти, чем обычное планарное решение.

Схематичный разрез двухмерной и трехмерной NAND Flash памятиСхематичный разрез двухмерной и трехмерной NAND Flash памяти

На электрической схеме выше транзисторы размещены последовательно, сверху вниз, тогда как в планарном варианте изготовления они расположены на плоскости, занимая ценную площадь на кристалле. Однако простая и монотонная структура позволила реализовать самое логичное, что можно сделать сквозной вертикальный канал транзистора, выглядящий примерно так же, как и электрическая схема (и показанный на схеме справ желтым, идущим сквозь зеленые затворы). Разумеется, оно только звучит логично и просто, а на практике создание глубокого отверстия с вертикальными стенками это одна из самых сложных операций, возможных в микроэлектронной технологии. Тем не менее, инженерные задачи были решены, и сейчас такие этажерки, как на рисунке выше, включают в себя уже до 128 транзисторов в серийно производимых чипах и до 192 слоев в девайсах, ожидаемых через год-два. Проектные нормы производства современной флэш-памяти примерно соответствуют уровню 15-20 нм, так что такая плотность упаковки это эквивалент норм 0.1-0.2 нм! В обычном КМОП повторить этот фокус в точности не удастся, но свежие исследования по GAAFET предполагают упаковку нескольких горизонтальных каналов друг поверх друга. Samsung рассчитывают таким образом выйти на уровень 1 нм, а то и чуть меньше.

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

Новые типы энергонезависимой памяти

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

Три наиболее зрелых технологии такого рода это MRAM (магнитная RAM), FRAM (ферроэлектрическая или сегнетоэлектрическая RAM) и PCM (phase-change memory).

FRAM основана на сегнетоэлектрическом эффекте свойстве некоторых материалов менять свои свойства под действием высокого напряжения. В частности, в FRAM используется изменение емкости конденсатора. Эта память появилась в серийных продуктах, например в микроконтроллерах MSP430, еще двадцать лет назад, то почти тогда же проявился ее главный недостаток пленки сегнетоэлектрических слоев оказались плохо масштабируемыми, и развитие технологии остановилось на уровне 130 нм, а существующие до сих пор продукты в основном довольно старые радиационностойкие микросхемы. Впрочем, в последние годы работы по сегнетоэлектрикам снова активизировались, и возможно нас ждет новое пришествие FRAM, на этот раз в виде FeFET, где из сегнетоэлектрика будет делать затвор транзистора.

Разные варианты MRAM используют несколько физических эффектов, позволяющих манипулировать спином магнитных материалов и посредством него, на их электрические свойства. По сути, мы говорим о физической реализации концепции мемристора резистора, сопротивление которого зависит от его предыдущего состояния. Первые серийные чипы MRAM появились еще в 2004, но проиграли технологическую войну флэш-памяти. Тем не менее, технология продолжает активно развиваться, подогреваемая тем, что у MRAM потенциально на несколько порядков большее количество циклов перезаписи, чем у флэш, а значит ее можно использовать как гибрид кэша и памяти долгосрочного хранения. Считается, что такое сочетание может быть востребовано в малопотребляющих микросхемах интернета вещей, и сейчас встраиваемые блоки MRAM предлагают такие именитые фабрики, как Samsung и GlobalFoundries.

PCM класс памяти, основанной на изменении фазового состояния некоторых веществ, например с кристаллического в аморфное, под действием внешних факторов типа высокого напряжения или кратковременного нагрева (обычно проводимого при помощи пропускания большого тока через запоминающий элемент). Потенциальные преимущества PCM примерно такие же, как у MRAM быстрое чтение и большое количество циклов перезаписи, что в теории может позволить заменить даже все три типа памяти одним унифицированным решением. На практике же изначальное внедрение PCM обернулось грандиозным провалом: в 2012 году Micron с помпой представили серийную линейку для применения в мобильных телефонах, однако уже к 2014 году все эти продукты были отозваны с рынка. Их вторая попытка стала более успешной совместно с Intel в 2017 году была представлена память 3D Xpoint и линейка SSD Optane (Intel) X100 (Micron). Продажи пока что невелики, но отзывы потребителей довольно хорошие. Посмотрим, выдержит ли новая технология проверку времени и сможет ли действительно потеснить традиционные SSD на основе NAND Flash.

Традиционная невеселая рубрика А что в России?

И, конечно же, мой рассказ был бы неполон без упоминания о том, что происходит в России. К сожалению, хорошего можно рассказать немного. Производство памяти это именно что производство, а с микроэлектронными заводами у россии довольно печальная ситуация. Соответственно, речи о собственных чипах DRAM и flash-памяти нет и в обозримом будущем не предвидится. А что есть?

Во-первых, есть какое-то количество микросхем SRAM. Самый технологически продвинутый продукт микросхема 1663РУ1, представляющая собой 16 Мбит статической памяти по нормам 90 нм, производства завода Микрон. Кроме этого чипа, есть и другие, в основном предназначенные для аэрокосмических применений.

Кроме статической памяти, есть еще одно производство Крокус-наноэлектроника, производящая MRAM. Расположенная в Москве фабрика КНЭ единственная в России, умеющая работать с пластинами диаметром 300 мм. Правда, Крокус-нано не обладает полным циклом производства, а может делать только металлизацию и совмещенные с ней магнитные слои, формирующие ячейку MRAM. Транзисторная часть при этом должна быть изготовлена на другой фабрике (иностранной, потому что в России с пластинами 300 мм работать некому). На сайте КНЭ заявлена доступность микросхем объемом от 1 до 4 Мбит, скоростью считывания 35 нс и записи 35/90/120/150 нс. Еще немного света на функционирование и происхождение этих чипов проливают также заявленные в качестве продуктов на официальном сайте сложнофункциональные блоки MRAM, совместимые с техпроцессами китайской фабрики SMIC и израильской TowerJazz. Вероятно, именно эти производители являются технологическими партнерами и при производстве собственных чипов КНЭ.

Последнее, о чем стоит упомянуть в контексте производства памяти в России это твердотельные системы хранения данных, являющиеся одним из важных драйверов импортозамещения в российской электронике. Российский рынок подобных систем оценивается в 122 миллиона долларов, доля отечественной продукции растет, идут жаркие споры насчет протекционистского законодательства и сравнения качества отечественных и импортных решений в общем, настоящая жизнь. К сожалению, как уже было сказано выше, о собственном производстве чипов NAND flash речи не идет, и под импортозамещением понимается сборка импортных чипов в корпус и на плату, а также разработка или адаптация встроенного ПО. Из хороших новостей в России ведутся разработки микросхем контроллеров флэш-памяти. Собственные контроллеры, хоть и не смогут решить проблему технологического отставания и зависимости от импорта, позволяют обеспечить контроль за функционированием импортных кристаллов памяти и безопасность решений на их основе. На этой позитивной ноте, я пожалуй, и закончу на сегодня.

Подробнее..

24x01 I2C на ATTINY13 без TWI

19.12.2020 14:20:36 | Автор: admin

Казалось бы что тут такого сложного, ну I2C ну без TWI.

Моя реальная задача обстояла чуть шире, в устройстве устанавливался чип в режиме "Reset" контроллера, для экономии места в программируемой логике, задача этого чипа состояла в том чтобы получив на входе короткий импульс как сигнал к началу генерации "длинного" сброса, чип удерживал одну из линий в течении довольно длительного промежутка времени в низком уровне (в программируемой логике можно было сделать цепочку из триггеров в качестве счетчика, но линейка триггеров занимала почти 80 ячеек в CPLD EPM240T100 и место там ой как нужно) к тому же этот чип должен был хранить данные полученный от внешнего устройства по шине I2C в режиме 1 (mode1).

Вообще существует три режима I2C, 2-ой и 3-ий режимы рассчитаны на то что на шине I2C висит гирлянда из устройств, таким образом прежде чем начать работать с устройством на шине в режиме 2 и 3 нужно сначала выбрать устройство, затем уже отправлять в него адреса и данные. Режим 1 это простейший режим работы I2C, пользователь сразу отправляет в чип адрес по которому необходимо записать или прочитать данные и работает с данными.

Вот собственно о чем я говорю (MODE1):

А это MODE2:

В общем-то тут всё итак понятно, но! Здесь есть три задачи:

  1. В ATTINY13 нет аппаратного I2C.

  2. Чип должен выполнять ещё и сброс (хотя для данной статьи это не особо важно).

  3. И финальная проблема 24x01 предполагает объем EEP 128 байт, а в данном чипе EEPROM объемом только 64 байта.

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

В общем я так понимаю зачастую лирика мало кого интересует, тут CTRC+C и CTRL+V чаще применяется, поэтому выкладываю уже код.

Только сперва Коментарии:

  1. CHIP_PINOUTS - Как расположить выводы на чипе (FT24Cxx - стандартная раскладка чипа I2C, InDRIVE_v4 - раскладка под реализацию с контроллером сброса).

  2. I2C_MODE - Режим работы I2C (MODE_1 и MODE_2).

  3. I2CPAGESIZE - размер страницы I2C при страничном режиме записи.

  4. I2CEEPSIZE - общий объем памяти чипа (реализация в коде сначала использует 64 байта EEPROM, затем растягивает "остатки" во FLASH).

  5. RESET_CNT - наличие контроллера сброса (ON - присутствует, OFF - отсутствует).

Собственно перейдем к проверке, достаем TLL866

Загружаем откомпилированную программу (AVR Studio v4), я пролистал в конец чтобы показать состав "псевдо EEP":

Ставим правильные конфигурационные биты и записываем микропрограмму в нашу ATTINY13:

Ага, а теперь не извлекая чип пишем его в режиме I2C EEPROM Mode1 с проверкой:

Работает. Что и требовалось.

Код I2C EEP для ATTINY13
/*InDRIVE v4 Application (chip like: FT24C01, Selfprg Must Be Enabled!!! )Fuse BYTESLow(0x7A) : High(0xEE) :SPIEN = 1SELFPRGEN = 1EESAVE = 0DWEN = 0WDTON = 0BODLEVEL1 = 0CKDIV8 = 0BODLEVEL0 = 0SUT1 = 0RSTDISBL = 1SUT0 = 1CKSEL1 = 0CKSEL0 = 1*/.include "tn13def.inc"/* On/Off Defines */.equOFF=0.equON=1/* Chip Pinouts */.equFT24Cxx=0.equInDRIVE_v4=1/* Reset Controller Enable/Disable */.equRESET_CNT=OFF/* Chip Pinouts Define: InDrivev4/Regular I2C Chip */.equCHIP_PINOUTS=FT24Cxx.equDEBUG=OFF/* I2C Mode Define MODE 1/MODE 2 */.equMODE_1=1.equMODE_2=2.EQUI2C_MODE=MODE_1/* Define I2C Parameters *//* Page Size for Write in BYTES 4/8/16 *//* 128/256 Size in MODE 1 Protocol can't receive more than 128 Bytes */.IF I2C_MODE == MODE_2.MESSAGE "Compile for I2C Mode 2".equI2C_PAGE_SIZE=8.equI2C_EEP_SIZE=256.ELSE.MESSAGE "Compile for I2C Mode 1".equI2C_PAGE_SIZE=4.equI2C_EEP_SIZE=128.ENDIF/* Modes DefineA - AddressiD - Data From MasteroD - Data To MasterMODE 1Write - [START][AAAAAAA0][iD + 0]...[iD + I2C_PAGE_SIZE - 1][STOP]Read -  [START][AAAAAAA1][oD + 0]...[oD + I2C_EEP_SIZE - 1][STOP]MODE 2 Write - [START][10100000][AAAAAAAA][iD + 0]...[iD + I2C_PAGE_SIZE - 1][STOP]Read -  [START][10100001][oD + 0]...[oD + I2C_EEP_SIZE - 1][STOP]*//*I2C slave, fSCL = 400kHzFULLY Implemented I2C Protocol for 24xx01/24xx02And RESET Controller FOR InDRIVE(v4)**************************************************************   ATTINY13A I2C Configuration**************************************************************      pin configuration InDRIVE v4:                       ,---_---.    (RESET/PB5)     nc |1     8| VCC          (PB3)  inRST |2     7| SCL  (PB2)          (PB4) outRST |3     6| EMU24X (PB1) SDA pin from CORE                   GND |4     5| nc (PB0) unused SDA pin                       `-------'   pin configuration FT24C01:                       ,---_---.    (RESET/PB5)     nc |1     8| VCC          (PB3)  nc |2     7| nc  (PB2)          (PB4) nc |3     6| SCL (PB1) SCL pin I2C                   GND |4     5| SDA (PB0) SDA pin I2C                       `-------'   pin configuration FT24C02:                       ,---_---.    (RESET/PB5)     A0 |1     8| VCC          (PB3)  A1 |2     7| nc  (PB2)          (PB4) A2 |3     6| SCL (PB1) SCL pin I2C                   GND |4     5| SDA (PB0) SDA pin I2C                       `-------'*//* Pins Define */.IF CHIP_PINOUTS == InDRIVE_v4.MESSAGE "Chip Pinouts: InDRIVE v4".equEMU24X= 1.equ SCL = 2.ELSE.equEMU24X= 0.equ SCL = 1.MESSAGE "Chip Pinouts: Regular 24Cxx".ENDIF.equ SDA = EMU24X.equ ACK = SDA.equinRST= 3.equoutRST= 4.defTMPnoINT=R19.defCounter=R3.defCounterInWrite=R4.defSREGST=R5/* Real ATTINY13A FLASH Page SIZE In Bytes */.equPAGESIZEB=(PAGESIZE*2)/* SRAM Mapping */.DSEG.IF I2C_MODE == MODE_2_I2c_device_inaddr:  .BYTE 1/* MODE 2 Region */_I2c_device_myaddr:  .BYTE 1.ENDIF_valSPMCSR: .BYTE 1_I2c_data_buffer:.BYTE I2C_PAGE_SIZE_I2c_FLASH_buffer:.BYTE PAGESIZEB.cseg/* Read/Write Pointers */.defI2c_start_addr=R16.defI2c_wr_counter=R17.defI2c_wr_pointer=R18.defI2c_rd_pointer=R21.def_PINB=R20.IF I2C_MODE != MODE_2.IF I2C_MODE != MODE_1.error "Invalid mode for I2C Selected, please correct I2C_MODE define".ENDIF.ENDIF.cseg.org 0//Reset.IF RESET_CNT == ONrjmpWaitinRSTHI;RESETreti;INT0addr= 0x0001; External Interrupt 0rjmpResetInProcess;PCI0addr= 0x0002; External Interrupt Request 0.ELSErjmpmain;RESETreti;INT0addr= 0x0001; External Interrupt 0reti;PCI0addr= 0x0002; External Interrupt Request 0.ENDIFreti;OVF0addr= 0x0003; Timer/Counter0 Overflowreti;ERDYaddr= 0x0004; EEPROM Readyreti;ACIaddr= 0x0005; Analog Comparatorreti;OC0Aaddr= 0x0006; Timer/Counter Compare Match Areti;OC0Baddr= 0x0007; Timer/Counter Compare Match Breti;WDTaddr= 0x0008; Watchdog Time-outreti;ADCCaddr= 0x0009; ADC Conversion Complete// *******************************************************************************************// **   Reset Processor                                                       **// *******************************************************************************************.IF RESET_CNT == ON.MESSAGE "Reset Controller Function is: ON"ResetInProcess:;Proccess resetsbicpinb,inRST;Process reset on falling edgeretiWaitinRSTHI:cli;Wait ~80 mS 0x04E360 on 9.6MHzldiR16,0x10ldiR17,0xE3ldiR18,0x60;Set RESET EnablecbiPORTB,outRST_CntLO:decR18BRNE_CntLO_CntME:decR17BRNE_CntLO_CntHI:decR16BRNE_CntLO; And Clear Iterrupt flag for normal exitldiR16,(1 << PCIF)outGIFR,R16.ELSE.MESSAGE "Reset Controller Function is: OFF".ENDIF// RESET_CNT == ON// *******************************************************************************************************// **   Main Programm                                                         **// *******************************************************************************************************main:;init STACKldiTMPnoINT,low(RAMEND)outSPL,TMPnoINT;init IOldiTMPnoINT,0x00outMCUCR,TMPnoINT;Outputs to HI (Pull UP)ldiTMPnoINT,0xFFoutPORTB,TMPnoINT.IF RESET_CNT == ON;Output enable for outRST, all other for inputsbiPORTB, outRSTsbiPINB, inRSTsbiDDRB,outRST; Output Enable;Init Interrupt    ldi TMPnoINT, (1 << inRST); set pin change interrupt for inRST    out PCMSK, TMPnoINTldiTMPnoINT, (1<<PCIE); unmask interrupt PCIEoutGIMSK,TMPnoINTSEI;Enable PCIE Int Processing.ELSECLI; Disable Reset Controller.ENDIF.IF I2C_MODE == MODE_2/* Set I2C Device Address */ldiTMPnoINT, 0xA0sts_I2c_device_myaddr,TMPnoINT.ENDIF/* Clear Buffer Pointers */clrI2c_wr_counterclrI2c_wr_pointerclrI2c_start_addrclrI2c_rd_pointermain_loop:lI2c_get:sbi portb,acksbis pinb,scl                                            ;wait for SCL&SDA=1rjmp lI2c_getsbis pinb,sdarjmp lI2c_getlI2c_wait_for_start:sbis pinb,scl                                            ;wait for SCL=1,SDA=0 (START)rjmp lI2c_getsbic pinb,sdarjmp lI2c_wait_for_startlI2c_get_0:                                                  ;clear receive area.IF I2C_MODE == MODE_2clr r22sts _I2c_device_inaddr,r22.ENDIFlI2c_get_1:                                                  ;get 1st bytesbi portb,acklI2c_10:                                                     ;wait for SCL=0sbic pinb,sclrjmp lI2c_10ldi r22,8                                                ;bits to receive=8.IF I2C_MODE == MODE_2lds r23,_I2c_device_myaddr                               ;I2C address->R23.ENDIFlI2c_11:in_PINB, pinbsbrs _PINB,scl                                            ;wait for SCL=1rjmp lI2c_11sbrc _PINB,sdarjmp lI2c_13lI2c_12:                                                     ;if SDA=0sbic pinb,sdarjmp lI2c_wait_for_start                                  ;  SDA 0->1? I2CSTOP! (unexpected: wait for next start)sbic pinb,sclrjmp lI2c_12                                              ;  loop while SCL=1clc                                                      ;  SDA=0->Crjmp lI2c_15lI2c_13:                                                     ;if SDA=1sbis pinb,sdarjmp lI2c_get_1                                           ;  SDA 1->0? I2CSTART! (repeated start)sbic pinb,sclrjmp lI2c_13                                              ;  loop while SCL=1sec                                                      ;  SDA=1->ClI2c_15:rol r24dec r22brne lI2c_11                                              ;loop to next bit.IF I2C_MODE == MODE_2sts _I2c_device_inaddr,r24SUB r24,r23                                              ;my address?cpi r24,2brlo lI2c_ack_1rjmp lI2c_exit                                            ;  no: exit and wait for next start.ENDIFlI2c_ack_1:                                                  ;  yes: generate ackclrI2c_wr_counter; Clear Write Buffer PointersclrI2c_wr_pointerclrI2c_start_addrcbi portb,acksbi ddrb,ack                                             ;pinb.ack = output (ACK)lI2c_ack_10:sbis pinb,scl                                            ;wait for SCL=1rjmp lI2c_ack_10lI2c_ack_11:sbic pinb,scl                                            ;wait for SCL=0rjmp lI2c_ack_11/**************************************************************************Select Read/Write**************************************************************************/.IF I2C_MODE == MODE_2cpi r24,0breq lI2c_get_2.ELSEmovI2c_start_addr,r24; Extract Received Addresslsr I2c_start_addr; And Read/Write BITmov I2c_rd_pointer,I2c_start_addr; Upadate Read Addressbrcs lI2c_send_new_byte rjmplI2c_ack_25.ENDIF/**************************************************************************Sending Data to Master**************************************************************************/lI2c_send_new_byte:                                             ;read address receivedsbi ddrb,sda                                             ;pinb.sda = output (will send data)/* Read From Flash */andi I2c_rd_pointer, (I2C_EEP_SIZE - 1)LDIZH,high(MemoryBlockFLASH<<1)LDIZL,low(MemoryBlockFLASH<<1)add ZL,I2c_rd_pointerinc I2c_rd_pointerlpmR24, Zldi r22,8.IF DEBUG == 1rjmp lI2c_s1/* Read From SRAM */ReadFromSram:andi I2c_rd_pointer, (I2C_EEP_SIZE - 1)LDIZH,high(_I2c_device_inaddr)LDIZL,low(_I2c_device_inaddr)add ZL,I2c_rd_pointerinc I2c_rd_pointerldR24, Zldi r22,8.ENDIFlI2c_s1:cbi portb,sdasbrc r24,7sbi portb,sdalI2c_s2:sbis pinb,scl                                            ;wait for SCL=1rjmp lI2c_s2lsl r24lI2c_s3:sbic pinb,scl                                            ;wait for SCL=0rjmp lI2c_s3dec r22brne lI2c_s1sbi portb,sda                                            ;pinb.sda = 0 (will generate ACK)cbi ddrb,sda                                             ;pinb.sda = input (will receive data)lI2c_s4:                                            ;wait acknowloge receivesbis pinb,scl                                            ;wait for SCL=1rjmp lI2c_s4lI2c_s5:sbic pinb,scl                                            ;wait for SCL=0rjmp lI2c_s5sbis pinb,sda;if answer received, - continuerjmplI2c_send_new_byterjmp lI2c_wait_for_start_stop                             ;wait for next start/stop/**************************************************************************Receiving Data from Master**************************************************************************/.IF I2C_MODE == MODE_2lI2c_get_2:                                                  ;write address received: get 2nd bytecbi ddrb,ack                                             ;pinb.ack = output (ACK)lI2c_20:                                                     ;wait for SCL=0sbic pinb,sclrjmp lI2c_20ldi r22,8                                                ;bits to receive=8lI2c_21:sbis pinb,scl                                            ;wait for SCL=1rjmp lI2c_21sbic pinb,sdarjmp lI2c_23lI2c_22:                                                     ;if SDA=0sbic pinb,sdarjmp lI2c_stop                                            ;  SDA 0->1? I2CSTOP! (finish this sequence)sbic pinb,sclrjmp lI2c_22                                              ;  loop while SCL=1clc                                                      ;  SDA=0->Crjmp lI2c_25lI2c_23:                                                     ;if SDA=1sbis pinb,sdarjmp lI2c_get_1                                           ;  SDA 1->0? I2CSTART! (repeated start)sbic pinb,sclrjmp lI2c_23                                              ;  loop while SCL=1sec                                                      ;  SDA=1->ClI2c_25:rol r24dec r22brne lI2c_21                                              ;loop to next bitmov I2c_start_addr,r24                                         ;store received I2C addressmov I2c_rd_pointer,I2c_start_addr; Upadate Read Address.ENDIFReceiveAcknowloge:cbiportb,sdasbiddrb,sdalI2c_ack_21:sbis pinb,scl                                            ;wait for SCL=1rjmp lI2c_ack_21sbic pinb,sdarjmp lI2c_ack_23lI2c_ack_22:                                                     ;if SDA=0sbic pinb,sdarjmp lI2c_stop                                            ;  SDA 0->1? I2CSTOP! (finish this sequence)sbic pinb,sclrjmp lI2c_ack_22                                              ;  loop while SCL=1rjmp lI2c_ack_25lI2c_ack_23:                                                     ;if SDA=1sbis pinb,sdarjmp lI2c_get_1                                           ;  SDA 1->0? I2CSTART! (repeated start)sbic pinb,sclrjmp lI2c_ack_23                                              ;  loop while SCL=1lI2c_ack_25:cbi ddrb,ack                                             ;pinb.ack = inputLDIZH,high(_I2c_data_buffer)LDIZL,low(_I2c_data_buffer)lI2c_get_3:                                                  ;get 3rd bytelI2c_30:                                                     ;wait for SCL=0sbic pinb,sclrjmp lI2c_30ldi r22,8                                                ;bits to receive=8movTMPnoINT, I2c_wr_pointerandi TMPnoINT, (I2C_PAGE_SIZE - 1)add ZL,TMPnoINTlI2c_31:sbis pinb,scl                                            ;wait for SCL=1rjmp lI2c_31sbic pinb,sdarjmp lI2c_33_lI2c_32:                                                     ;if SDA=0sbic pinb,sdarjmp lI2c_stop_                                            ;  SDA 0->1? I2CSTOP! (finish this sequence)sbic pinb,sclrjmp lI2c_32                                              ;  loop while SCL=1clc                                                      ;  SDA=0->Crjmp lI2c_35lI2c_33_:lI2c_33:                                                     ;if SDA=1sbis pinb,sdarjmp lI2c_start_                                           ;  SDA 1->0? I2CSTART! (repeated start)sbic pinb,sclrjmp lI2c_33                                              ;  loop while SCL=1sec                                                      ;  SDA=1->ClI2c_35:rol r24dec r22brne lI2c_31                                              ;loop to next bitinc I2c_wr_counterinc I2c_wr_pointerinc I2c_rd_pointerstZ,R24rjmp ReceiveAcknowlogelI2c_wait_for_start_stop:                                    ;wait for start/stoplI2c_ss_1:sbis pinb,scl                                            ;wait for SCL=1rjmp lI2c_ss_1sbic pinb,sdarjmp lI2c_ss_3lI2c_ss_2:                                                   ;if SDA=0sbic pinb,sdarjmp lI2c_stop                                            ;  SDA 0->1? I2CSTOP! (finish this sequence)sbic pinb,sclrjmp lI2c_ss_2                                            ;  loop while SCL=1rjmp lI2c_ss_1lI2c_ss_3:                                                   ;if SDA=1sbis pinb,sdarjmp lI2c_get_1                                           ;  SDA 1->0? I2CSTART! (repeated start)sbic pinb,sclrjmp lI2c_ss_3                                            ;  loop while SCL=1rjmp lI2c_ss_1                                            ;  SDA=1->ClI2c_stop_:lI2c_stop:                                                   ;if stop,cbiddrb, sdacpiI2c_wr_counter,0x00brne WriteFlashRomlI2c_exit:rjmp lI2c_getlI2c_start_:rjmp lI2c_get_1WriteFlashRom:cli; Disable INTERRUPTSrcall WriteReceivedDataclrI2c_wr_counter; Clear Write Buffer PointersclrI2c_wr_pointersei; Enable INTERRUPTSrjmp lI2c_get/************************************************************ Write internal FLASH by page************************************************************///--------- Erase/Program Page FROM FLASH to SRAMBackupFlashPage:ldiYH,high(_I2c_FLASH_buffer) ; Load SRAM Buffer ldiYL,low(_I2c_FLASH_buffer)movTMPnoINT, I2c_start_addrandiTMPnoINT, ((I2C_EEP_SIZE - 1) & ~(PAGESIZEB - 1))ldiZH,high(MemoryBlockFLASH << 1)ldiZL,low(MemoryBlockFLASH << 1)adcZL,TMPnoINTbrccNoIncBackupPageincZHNoIncBackupPage:ldiR22, PAGESIZEBBackupPageLoop:lpmTMPnoINT,Z+stY+,TMPnoINTdecR22brneBackupPageLoopret//--------- Add received data to SRAM PageWriteReceivedData:/* Have Data For Write ? */cpiI2c_wr_counter,0x00brneDataForWritePresentretDataForWritePresent:rcall BackupFlashPageldiYH,high(_I2c_FLASH_buffer) ; Load SRAM Backup Buffer AddressldiYL,low(_I2c_FLASH_buffer)ldiXH,high(_I2c_data_buffer) ; Load SRAM Receive Buffer AddressldiXL,low(_I2c_data_buffer)movTMPnoINT, I2c_start_addrandiTMPnoINT, (PAGESIZEB - 1)adcYL,TMPnoINTbrccNoIncPreparePageincYHNoIncPreparePage:movCounterInWrite,TMPnoINT/* Make PAGE SIZE Window */cpiI2c_wr_counter, I2C_PAGE_SIZEbrloPreparePageLoopldiI2c_wr_counter, I2C_PAGE_SIZEPreparePageLoop:ldR22,X+stY+,R22decI2c_wr_counterbreqNormalPageWritemovTMPnoINT,CounterInWritecpiTMPnoINT, (PAGESIZEB - 1)brneNoWritePageSizeExceededrcallErase_page_by_SPMrcallWrite_Current_PageincCounterInWriteincI2c_start_addrdecI2c_wr_pointerrjmpWriteReceivedDataNoWritePageSizeExceeded:incCounterInWriteincI2c_start_addrdecI2c_wr_pointerrjmpPreparePageLoopNormalPageWrite:rcallErase_page_by_SPMrcallWrite_Current_PageincI2c_start_addrdecI2c_wr_pointerret//--------- Erase pageErase_page_by_SPM:ldiZH,high(MemoryBlockFLASH << 1)ldiZL,low(MemoryBlockFLASH << 1)movTMPnoINT,I2c_start_addrandi TMPnoINT, ((I2C_EEP_SIZE - 1) & ~(PAGESIZEB - 1)) /* Mask Maximum Data Size and Mask Out Real Flash Page Size */adcZL,TMPnoINTbrccNoIncErasePageincZHNoIncErasePage:/* Load Erase Instruction */ldiTMPnoINT, (1<<PGERS) | (1<<SPMEN)sts_valSPMCSR, TMPnoINTrcallWait_spm;CPU Halted while eraseret//Wite pageWrite_Current_Page:ldiXH,high(_I2c_FLASH_buffer) ; Load SRAM Backup Buffer AddressldiXL,low(_I2c_FLASH_buffer)ldiZH,high(MemoryBlockFLASH << 1)ldiZL,low(MemoryBlockFLASH << 1)movTMPnoINT,I2c_start_addrandi TMPnoINT, ((I2C_EEP_SIZE - 1) & ~(PAGESIZEB - 1)) /* Mask Maximum Data Size and Mask Out Real Flash Page Size */adcZL,TMPnoINTbrccNoIncWritePageincZHNoIncWritePage:ldiTMPnoINT, (PAGESIZE)movCounter, TMPnoINTmovwY,Z; Store Z in Y regclrZL;Clear ZclrZHFill_Page_Buffer_loop:ldR0, X+ldR1, X+ldiTMPnoINT, (1<<SPMEN);Write word into page buffersts_valSPMCSR, TMPnoINTrcallWait_spm;CPU Halted while eraseadiwZ, 2decCounterbrneFill_Page_Buffer_loopmovwZ,Y; Restore Z from Y regldi TMPnoINT, (1<<PGWRT) | (1<<SPMEN)sts_valSPMCSR, TMPnoINTrcallWait_spm;CPU Halted while eraseretDo_spm:; check for previous SPM completeWait_spm:inTMPnoINT, SPMCSRsbrc TMPnoINT, SPMENrjmp Wait_spmWait_ee:sbic EECR, EEWErjmp Wait_ee; SPM timed sequenceldsTMPnoINT, _valSPMCSRout SPMCSR, TMPnoINTspmret.cseg.org(0x200 - (I2C_EEP_SIZE/2))MemoryBlockFLASH:          ; Bytes in Flash   .DB 0x55, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F   .DB 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F   .DB 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F   .DB 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F   .DB 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F   .DB 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F   .DB 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F   .DB 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x7B, 0x7C, 0x7D   ; Mark End of EEP DATA.org(0x1FF).db 0x7E,0x7F.MESSAGE "I2C For Read/Write Processing SELFPRGEN FUSE Must Be Enabled!!!".esegMemoryBlockEEP:          ; Bytes in EEP   .DB 0x55, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F   .DB 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F   .DB 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F   .DB 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F

Всем добра и успехов!!!

Спасибо за внимание.

Подробнее..

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru