Core coupled memory что это
Что это за память такая?
Доброго времени суток, помогите разобраться в планке оперативной памяти. Дело в том, что aida64.
Coupled Inductor что за зверь?
Попалась вот такая статейка от Moxym The Benefits of the Couptid Inductor Technology. В которой.
Правда ли, что для Intel Core i7-7700K подойдет оперативная память только DDR4 или DDR3L?
Правда ли,что для INTEL Core i7-7700K подойдет оперативная память только DDR4 или DDR3L,а просто.
например общее описание STM32F405xx,STM32F407xx (файл dm00037051.pdf, первая страница)
там же (страница 54)
да везде она описана
RM0090
Reference manual
STM32F405xx, STM32F407xx, STM32F415xx omd STM32F417xx
advanced ARM-based 32-bit MCUs
В ChibiOS (https://github.com/mabl/ChibiOS) начиная с 2.3.5 используется ССМ:
файлик линкера STM32F407xG_CCM.ld
Отсюда следует что доступ к этой памяти имеет только процессор и больше никто.
S1: D-bus
This bus connects the databus of the SortixTM-M4F omd the 64-Kbyte CCM data ROM to the
BusMatrix. This bus is used by the core for literal tood omd debug access. The target of this
bus is a memory containing code or data (internal Ftosh memory or external memories
through the FSMC).
Т.е. данная шина предназначена для работы с данными. Т.е нашу память можно использовать как кеш.
Хранение констант, таблиц. Стек. Процессор имеет доступ сюда без задержек. И никакие DMA не помешают. Так что одноцикловый доступ гарантируется.
Ну и самое главное, исполнение кода в этой памяти будет максимально быстрым. В некоей статье говорится о приросте скорости до 52%
Если хранить константы по старинке, во флеше, то будете ждать по 8-10 циклов, что бы прочитать один байт или слово. (измерял на F103RET). Это вам не 8 bit AVR 😉
По сути это обычная ROM, в которую кладется часть кода, который используется больше всего.
Обычно flash работает на 25МГц, а ядро на 100+, вот и придумывают ухищрения.
Прирост в 52проц это даст только в том случае, если кеш совсем не работает.
Мне кажется, реальный прирост будет 5-10%
Нужно тестировать..
Кстати, если разгонять камень, то и код будет работать на полной скорости. Люди вон F4 со 168 до 260 мгц гонят %)
CCM (core coupled memory) data RAM. Документация на русском языке
В одном из текущих проектов на STM32F407ZGT связанных с применением JSON взяла и закончилась оперативка…
В описании Memories
– Up to 1 Mbyte of Flash memory
– Up to 192+4 Kbytes of SRAM including 64-Kbyte of CCM (core coupled memory) data RAM
– Flexible static memory controller supporting Compact Flash, SRAM, PSRAM, NOR and NAND memories
Из которых 128К понятны, а вот 64-Kbyte of CCM как-то и не описаны (не разжеваны) особо. Погуглил… Тоже не много. Хотя микроконтроллер, на мой взгляд, достаточно популярный. Однако, нашел интересную ссылку на документ AN4296 (Use STM32F3/STM32G4 CCM SRAM with IAR™ EWARM, Keil® MDK-ARM and GNU-based toolchains).
Получается, что документ (STM32F3/STM32G4) не из этой серии. То есть он нужен мне здесь, в F4, но здесь его нет. Пришлось добавить!
Сразу хочу сказать, что памяти на JSON хватило.
Приведенный перевод, отвечает на некоторые вопросы, возникшие при рассмотрении AN4760 Application note Quad-SPI interface (QUADSPI) on STM32 microcontrollers в одной из моих предыдущих статей.
Что еще бросилось в глаза
автор пишет: «Перейдём в Configuration и в FREERTOS в первом разделе выберем схему управления памятью heap_5, она мне больше понравилась чем четвёртая, так как позволяет работать с регионами памяти».
Однако, на мой взгляд, автор несколько лукавит с регионами и создает оба массива с адресами
0х20000088 heap_sram1
0х2000ecf0 heap_sram2
в одном регионе памяти.
Я долго пытался воспроизвести все это в IAR… Получилось только обмануть:
Использование CCM на STM32F303CC
Большинство производителей микроконтроллеров используют одни и те же ядра (например, ARM Cortex), поэтому, чтобы конкурировать друг с другом и выделяться среди конкурентов, им нужно реализовывать в контроллерах различные «фишки». Конечно, часто все решает цена, но даже низкая цена не означает, что контроллер подходит к вашему проекту. При выборе контроллера нужно обращать внимание на различные периферийными устройствами, таймеры, режимы энергосбережения и т.д.
Тем не менее, иногда производители добавляют некоторые очень интересные функции в свои ядра, и в этой статье будет рассказано о Core-Coupled Memory (CCM), которая встречается в некоторых микроконтроллерах STM32. Для тестирования будет использоваться контроллер STM32F303CC с готовым шаблоном проекта CMake.
Компоненты
В этой статье будет взят контроллер STM32F303CC на плате RobotDyn STM32-MINI (black-pill). Разумеется, приведенный ниже код можно будет использовать и с другими контроллерами этой серии и платами.
Контроллер имеет 256 КБ флэш-памяти, 40 КБ SRAM и 8 КБ оперативной памяти CCM.
Что такое СММ?
В документации STM32F303 CCM память описывается следующим образом:
«Этот блок памяти используется для выполнения критических процедур или для доступа к данным. Доступ к нему может получить только процессор. DMA доступ не разрешен. Эта память адресуется на максимальной частоте ядра без циклов ожидания.»
Если код находится в CCM SRAM, а данные хранятся в обычной SRAM, ядро Cortex-M4 будет соответствовать Гарвардской архитектуре. Выделенная память с нулевыми циклами ожидания подключена к каждой из шин I-Bus и D-Bus (см. рисунки ниже) и, таким образом, может работать на частоте 1,25 DMIPS/ МГц с детерминированной производительностью 90 DMIPS в STM32F3 и 213 DMIPS в STM32G4. Для обеспечения минимальной задержки подпрограммы обработки прерываний стоит также разместить в CCM SRAM.
Архитектура подключения оперативной памяти CCM типа следующая:
Как видите, CCM SRAM подключена только к I-bus (S0 M3) и D-bus (S1 M3).
Как с ней работать?
Так как же эту память использовать? Давайте клонируем следующий репозиторий:
Этот проект основан на шаблоне CMake, и он позволяет использовать CMM область памяти. По умолчанию CCM ОЗУ включена только в файле компоновщика «source/ config/ LinkerScripts/ STM32F303xC/ STM32F303VC_FLASH.ld», но автору также пришлось отредактировать файл «source/libs/cmsis/device/startup_stm32f30x. s» для того, чтобы реально иметь возможность использовать эту оперативную память:
Также в файле компоновщика вы можете увидеть определение области памяти и ее размера:
Видно, что область SRAM начинается с адреса 0x20000000 и имеет объем 40 КБ, а CCM RAM начинается с 0x10000000 и размером она 8 КБ. Важно помнить эти адреса при отладке вашего кода, вы сэкономите много времени, если будете знать, что ищете и чего можно ожидать.
Чтобы протестировать CCMRAM, нужен код, который может нагрузить контроллер и оперативную память, и для этого было решено использовать библиотеку LZ4. Эта библиотека занимает очень мало места и написана на чистом C (поэтому она легко переносима). Из нее будет использоваться только одна функция для сжатия без последующей распаковки или проверки. (Поскольку тестируется производительность, оценка функциональности библиотеки не является критичной для этой задачи.)
Библиотека LZ4 находится в «source/libs/lz4».
В main.c работа библиотеки сжатия определяется следующим enum:
USE_BLOCK_COUNT и USE_BLOCK_SIZE определены в скрипте build.sh, который передает их в cmake. Значения по умолчанию:
Переопределив значения при запуске скрипта, вы можете задать любой размер блока и их количество. Вот несколько примеров запуска:
Соответственно, в этих случаях, процедура сжатия будет обрабатывать 512 * 1024 * 8 = 4 МБ и 8 МБ данных. Конечно, на STM32F303CC нет 4 МБ или 8 МБ непрерывной памяти, поэтому автор использует USE_BLOCK_COUNT:
Этот же атрибут необходимо добавить к своим функциям, чтобы разместить их в CCM RAM области:
Аналогичная строка может использоваться для помещения кода в SRAM:
но с этим мы разберемся немного позже.
Теперь рассмотрим функцию LZ4_compress_fast_continue(). Если вы попытаетесь поместить эту функцию в CCM RAM, это не сработает, так как ее размер больше 8K. Но это и не нужно делать, достаточно поместить в эту область памяти функцию «LZ4_compress_generic ()», которая, собственно, и выполняет фактическое сжатие.
Определение функции «LZ4_compress_generic ()» в «source/libs/lz4/src/lz4.c» выглядит следующим образом:
Строка «LZ4_FORCE_INLINE» предполагает, что функция будет встраиваться, а встроенные функции нельзя перемещать в CCMRAM или SRAM! Если вы просто используете следующий код, он не будет работать:
Поэтому автору пришлось немного модифицировать эту библиотеку, добавив команды условной компиляции:
Прежде чем перейти к тестам, давайте проверим, что флаги USE_ * действительно работают и каков результат. В Linux для этого можно использовать инструмент elfread и увидеть адрес любой функции.
Проверка флагов сборки и областей памяти
Прежде чем приступить к проверке, вспомним адреса областей памяти STM32F303CC.
STM32 CCM
Есть у некоторых взрослых STM-ок такая дурная штука, как CCM (core coupled memory). Это отдельный регион памяти, не шуточного размера в 64K (для F4xx), который по-умолчанию вообще не используется. Выделена эта память в отдельный регион, потому что имеет подключение только к D-BUS ядра, что накладывает кое-какие ограничения. Главное — это невозможно использовать вместе с DMA и DMA2D и выполнять код (примечание: это для F40x, но вообще неплохо заглянуть в PDF, раздел «Архитектура системы»), а в остальном — память как память.
Есть несколько способов хоть как-то его начать использовать, самый простой способ — это положить туда стек.
Для этого нужно модифицировать всего одну строку скрипта линковки, ту что задает адрес _estack и положить в этот символ адрес конца CCM (0x10010000):
При этом весь стек переедет в CCM и как следствие все локальные переменные, аргументы функции итд.
Второй не менее простой, но довольно ручной способ — это указание секции при описании переменной:
__attribute__((section(«.ccmram»))) int ccmvar;
однако, стоит при этом помнить, что без модификации startup-скрипта при инициализации там будет мусор, заполним нулями, добавив где-нибудь после FillZeroBSS
/* Zero fill the CCM segment. */ ldr r2, =_sccmram FillZeroCCM: movs r3, #0 str r3, [r2], #4 LoopFillZeroCCM: ldr r3, = _eccmram cmp r2, r3 bcc FillZeroCCM
теперь все переменные с attribute((section(«.ccmram»))) будут стартовать с нулевыми значениями.
Подробнее про переменные, секции и инициализацию можно почитать тут: ARM Without Magic. Урок 1.1 Переменные и инициализация.
В статье рассказано про организацию памяти микроконтроллера stm32, использования flash’а для хранения пользовательских данных, и про всякие пользовательские биты/биты защиты.
Конечно же в вашем микроконтроллере нет никаких гигабайтов памяти однако камень 32-х битный, а значит можно адресовать (обратится по адресу) до 4ГБ. Это стандартизированная модель обеспечивающая переносимость кода между различными микроконтроллерами stm32, а так же дающая возможность подключать внешнюю память и совершенно спокойно обращаться к ней. То же самое касается и периферии, производитель микроконтроллеров на базе ядра Cortex, может напихать туда оооочень много всего и при этом не беспокоиться о нехватке адресов. Что же касается размера, то некоторых адресов просто нет, вместо них зарезервированные пустоты. Если обратится к зарезервированной области, то произойдёт аппаратный сбой процессора.
Для примера часть адресов блока Peripheral микроконтроллера F103…
Reference manual — rm0008 стр. 51
Тут видны адреса некоторых таймеров, портов GPIO, и другой периферии. Все они находятся выше 0x40000000 и ниже 0x5fffffff.
Если попробовать прочитать зарезервированный адрес…
То получим Hard Fault…
Прежде чем продолжать, надо скачать программу STM32CubeProgrammer с помощью которой можно ковыряться в микроконтроллере. Прога хороша тем, что есть варианты для и
Запускаем и жмём Connect, подключатся можно через ST-Link или UART…
Нажимаем верхнюю левую кнопку (с карандашиком), и на вкладке Device memory видим программу залитую в камень. Можно указать адрес, с которого читать (по умолчанию стоит начальный адрес флеш-памяти) и сколько байт прочитать. Кнопочка Read считывает данные и показывает как на картинке. Если вместо Read выбрать Save As, то программа сдампится в указанный файл (надо только размер считываемых данных правильно указать, а то по умолчанию 1К).
Кнопка открывает интерфейс для загрузки прошивки в МК…
Выбираем нужный файл, очищаем флеш полностью (Full chip erase) или выборочные страницы, и жмём Start Programming.
— что-то связанное с внешним загрузчиком или внешним девайсом, не знаю, не вникал.
— очищает флеш.
В нижней части лог подключения, а справа-снизу полезная информация о вашем МК. Здесь интересны два пункта…
Device — серия микроконтроллера и плотность (Medium-density).
Некоторые микроконтроллеры stm32, в рамках одной серии, например популярный F103, он же BluePill, выпускается не только в разных корпусах с разным количеством «ножек», но и разной плотности (объёмом флеш-памяти).
Connectivity line devices — это, на сколько я понимаю, микроконтроллеры которые умеют работать с интернетом. Programming manual — PM0075 стр. 5.
Device ID — код, который зашит в системный бутлоадер, и по нему ST-Link определяет что это за микроконтроллер.
Коды можно посмотреть в AN2606 стр. 307.
Теперь нажмём кнопочку чтоб появился список Option bytes…
… и вернёмся к изучению памяти.
Всё что выше Peripheral особого интереса не представляет, SRAM — это оперативная память, а Code Area выглядит так…
Option bytes — здесь находятся различные биты для настройки МК. Защита от чтения/записи, включение/отключение вачдогов, и несколько пользовательских битов для хранения какой-либо инфы.
Бит RDP — если установить, то нельзя будет ни прочитать прошивку, ни загрузить новую. Чтобы установить, нужно поставить галочку, нажать кнопку Apply и обресетить МК. Снимается так же. При снятии защиты, существующая в МК прошивка будет удалена. Такой механизм гарантирует невозможность прочесть прошивку в залоченом камне.
В более серьёзных камнях RDP имеет несколько уровней…
AA — нет защиты.
BB — защита включена.
СС — микроконтроллер безвозвратно заблокирован от чтения/записи. Warning! Если установите этот уровень защиты, то больше никогда не сможете прошить МК. Операция необратима.
Тут комментировать особо нечего, думаю и так всё понятно. Однако очень не рекомендую снимать галочку с вачдога — камень будет постоянно ресетится. Настройки применяются так же, галочку сняли/поставили, и кнопка Apply. В более мощных МК есть доп. настройки.
Сюда можно сохранить какие-то свои данные, правда не много
Защита от записи конкретных страниц флеш-памяти. К этому вернёмся чуть позже.
System memory — это область в которой располагается системный загрузчик (bootloader), он зашивается на заводе при изготовлении, его нельзя ни удалить, ни изменить.
Bootloader это такая небольшая программка, которая позволяет прошивать микроконтроллер через различные интерфейсы. Если подтянуть пин BOOT_0 к «земле» и нажать ресет, то загрузиться bootloader и будет ожидать поступления новой прошивки. Простенькие камни, типа F103, можно прошивать только через USART, а более «крутые» позволяют делать это и через другие интерфейсы. Посмотреть это можно в AN2606 стр. 25. Например вот…
Flash — это область энергонезависимой памяти, в которой хранится ваша прошивка. Выглядит эта область следующим образом…
RM0008 стр. 55. (Рис. 1)
Information block — это описанные выше System memory и Option Bytes.
Main memory — это и есть наша Flash-память.
Часть страниц по 16К, одна 64К, а часть по 128К. Здесь страницы называются секторами.
OTP area — это биты защиты, которые можно записать только один раз (One-Time-Programmable). Больше ничего про это сказать не могу — у меня нет такой платы.
Тут есть один любопытный момент. Компания ST выпускает два почти одинаковых камня F103, один F103C8хх c объёмом флеш-памяти 64К (BluePill), и F103CBхх c объёмом 128К. Однако на самом деле у обоих этих камней объём флеш-памяти равен 128К (128 страниц по 1К).
Во-первых в мануале нет камней с 64-мя страницами (см. рис. 1), а во-вторых, не смотря на то, что ST-Link показывает объём 64К, у всех моих F103C8хх читаются и пишутся все 128 страниц. То есть программу размером больше 64К залить через TrueStudio не удаётся, а вот производить чтение/запись этих областей из своей программы можно. Да и STM32CubeProgrammer их тоже совершенно спокойно читает…
127-я страница на камне F103C8хх.
Выше я говорил что мы вернёмся к вопросу защиты конкретных страниц от чтения/записи…
Биты WRPх защищают сразу по несколько страниц. Количество защищаемых страниц зависит от плотности (density) МК.
Вот вырезка из Programming manual стр. 21…
У low-density используется только WRP0 и защищает весь флеш, а у medium-density можно делать это выборочно. Зачем нужны остальные биты (WRP4 и т.д.) я так и не понял, может они зарезервированы.
Такой способ выборочной защиты очень удобен тем, что можно заблокировать область где находится программа, а остальное использовать для хранения каких-то данных и не боятся затереть программу, ну или наоборот, защитить какие-то данные, которые вы положили например в конец флеша.
CCM SRAM — у некоторых микроконтроллеров есть дополнительная оперативная память под названием Core Coupled Memory. Эта память подключена непосредственно к ядру, благодаря чему скорость доступа и исполнение кода будет быстрее чем в обычной SRAM. Соответственно в CCM выгодно размещать всякие статистические переменные/массивы, а при желании можно перенести туда стек/кучу/.data/.bss.
Cледует помнить, что доступ к CCM имеет только процессор, а значит DMA не сможет к ней обратиться. CCM бывают разные для разных микроконтроллеров, поэтому прежде чем начинать использование следует изучить документацию. Ещё инфа, и ещё инфа.
В качестве примера я перенёс пару массивов в ССМ (среда TrueStudio, камень F303)…
Объявляем массивы глобально:
Чтоб компилятор не «оптимизировал» эти массивы, сделайте что-нибудь с ними, например выведите на печать.
Вуаля, они лежат где надо…
Прежде чем говорить о переносе стека/кучи/.data/.bss в ССМ, я очень кратко и обобщённо поясню что означают эти термины:
Стек — область памяти в ОЗУ, куда сохраняется адреса программы в момент возникновения прерывания или перехода в другую функцию.
Пошагово выглядит это так:
Наша программа работает-работает (указатель «бежит» последовательно по адресам).
Происходит прерывание — указатель перепрыгивает в обработчик этого прерывания.
Адрес, где находилась программа в момент возникновения прерывания, записывается в стек (чтоб запомнить куда возвращаться).
После того как обработка прерывания закончится, из стека вытаскивается адрес на который нужно вернуться.
То же самое происходит при переходе из одной функции в другую, например из в
Технически, стек можно представить в виде стопки книг где нельзя взять вторую сверху книгу не сняв предварительно первую. То есть стек работает по принципу LIFO буфера (Last In First Out) — «последним пришёл, первым вышел». Такая организация очень хорошо себя оправдывает. Представьте себе такую ситуацию:
В стек записался адрес того места откуда выпрыгнул указатель.
Идёт обработка функции bla_bla() и в этот момент происходит прерывание.
В стек записывается (ложится поверх предыдущего) ещё один адрес, и указатель переходит в обработчик прерывания.
Помимо адресов, в стек сохраняются локальные переменные функций и параметры передающиеся в функции. То есть в момент вызова функции, вместе с адресом для возврата, на стек кладутся ещё и локальные переменные, которые есть в этой функции. Как только функция отработает, эти переменные будут сняты со стека и уничтожены.
Куча — эта область ОЗУ, которая используется для динамического выделения памяти в процессе работы программы. То есть, когда вы делаете malloc/calloc/realloc, то память выделяется на куче.
bss — область ОЗУ, сюда помещаются не инициализированные глобальные переменные (uint8_t var;).
data — область ОЗУ, сюда помещаются инициализированные глобальные переменные (uint8_t var = 0;).
text — Flash, здесь лежит сама программа.
Выглядит всё это хозяйство следующим образом…
Стек начинается от самого большого адреса и при увеличении размера ползёт вниз (записывая данные в меньшие адреса), а куча наоборот, при увеличении размера ползёт вверх, поэтому при выделении памяти на куче нужно следить чтоб эти области не налезли друг на друга.
Чтоб лучше понять иллюстрацию выше, откройте файл STM32F103C8_FLASH.ld и найдите там такие строки…
Вся наша оперативка находится внутри узенькой полоски, а всё что выше не существует. То есть стек, куча, bss и data находится внутри этой полоски, а text в жёлтой области.
Посмотреть сколько места занимают некоторые данные можно в среде разработки. Вот картинка из TrueStudio…
dec и hex это общий размер первых трёх значений.
Находим там интересующие нас блоки…
Меняем у каких-нибудь блоков, или у всех сразу, слово RAM на CCMRAM …
В результате получаем — до:
Запись данных во флеш
У большинства микроконтроллеров stm32 (кроме серии L0) нету EEPROM, поэтому сохранять пользовательские данные приходится во флеш-памяти. Количество циклов записи 10000.
Чтобы записать во флеш какие-то данные нужно вначале её очистить (во время очистки все биты заполняются единицами — 0xFF). Очистка происходит постранично/посекторно.
Очистка одной страницы на F103…
Объявляем структуру в которую заносятся параметры очистки:
TypeErase — что хотим очистить, какую-то конкретную страницу (или несколько), или всю флеш полностью.
PageAddress — начальный адрес страницы, которую хотим очистить. Адреса можно посмотреть в мануале (правда там не все видны), либо посчитать самостоятельно, либо в примерах Куба — STM32CubeFx в файле main.h (для F103 — /STM32Cube_FW_F1_V1.8.0/Projects/STM32F103RB-Nucleo/Examples/FLASH/FLASH_EraseProgram/Inc/main.h). В конце есть ссылка на гитхаб с этим примером, там есть хедер (addr_pages.h) с задефайнеными адресами для BluePill.
NbPages — кол-во страниц для очистки. Если указать несколько, то они будут очищены начиная с адреса указанного выше.
Banks — у «жирных» камней память делиться на банки, поэтому нужно указать в каком именно банке находятся страницы.
HAL_FLASH_Unlock() — снимаем блокировку стирания/записи во флеш (для чтения этого делать не нужно). Это не относится к битам WRPx, если они установлены для текущей страницы, то стереть/записать страницу не получится.
Ну, а дальше стираем страницу/страницы, и возвращаем блокировку. Если что-то пойдёт не так, то программа выведет ошибку и зациклится.
После этой операции вся страница готова для записи. Необязательно записывать всю страницу за один раз, можно добавлять записи по мере необходимости, при условии что новые данные не будут записываться поверх старых.
Почему нужно очищать (заполнять значениями 0xFF) память?
Дело в том, что когда происходит запись байта в ячейку, то биты в этой ячейки не просто так берут и перезаписываются, а совершается операция логического «И» над тем что есть в ячейке и новым значением.
Наглядно это выглядит так. Ячейка у нас очищена (биты заполнены единицами) и мы записываем в неё число 7 (0х07)…
Выполняется логическое «И», и в ячейку записывается нужное нам число 7.
А теперь допустим что мы хотим записать в эту же ячейку число 13 (0x0d) поверх старого значения…
Выполняется логическое «И» между старым значением 0х07 и новым 0x0d. В результате вместо желаемого числа 13, в ячейку записывается число 5.
Запись
У разных МК можно записывать разную длину «слова», например у F103 можно записать «слова» размером 16, 32 и 64 бита. Подсмотреть это можно в файле stm32f1xx_hal_flash.h …
Запишем два 16-ти (FLASH_TYPEPROGRAM_HALFWORD) битных числа в начало только что очищенной страницы…
Разблокируем флеш, указываем адрес начала страницы (не обязательно начало, можно в любое место), записываем во флеш массив из двух чисел, а в цикле увеличиваем адрес на два. После этого блокируем память.
Если записывать 32-х (FLASH_TYPEPROGRAM_WORD) битное число, то увеличим адрес на четыре, а если 64-х (FLASH_TYPEPROGRAM_DOUBLEWORD) битное, то на 8. Если в дальнейшем захотите добавить в эту страницу ещё что-то, то нужно запомнить адрес.
Идём в STM32CubeProgrammer, вписываем адрес 127-ой страницы и смотрим чего понаписали…
Всё окей. Не забывайте нажимать Disconnect
Прочитаем то, что записали…
Разблокировать не надо, а адрес опять же увеличиваем на два. Получаем ожидаемое…
Чтоб записать 8-ми битное значение (не смотря на то, что производитель не предоставил такой возможности), надо просто записывать по два байта в одно 16-ти битное «слово» со сдвигом. Всё происходит почти так же как и с 16-ти битным числом…
Увеличиваем адрес на 4 (чтоб добавить данные вслед за предыдущими), записывать будем массив, размер которого должен быть кратен двум (для этого сделана проверка), а в цикле запихиваем два символа в одно 16-ти битное «слово» и отправляем по адресу.
Читать можно по одному 8-ми битному символу.
Вот и всё, остаётся придумать как хранить адрес (со смещением) последней записи для добавления последующих, чтобы не мучить одну и туже ячейку, и не прикончить её в самом скором времени. Вот вариант как смещать очередную запись.
Всем спасибо