1

Тема: Організація пам'яті EEPROM AT24C512

Всім привіт.

Допоможіть будь ласка розібратись як організована пам'ять в EEPROM AT24C512. Я зовсім заплутався. В мене повний ступор.

Дивлюсь в datasheet і в 5 розділі "Memory Organization" написано: AT24C512C, 512-Kbit Serial EEPROM: The 512K is internally organized as 512 pages of 128 bytes each.

Добре, це я розумію так: що eeprom має 512 сторінок по 128 байтів. Запис в пам'ять має бути в межах однієї сторінки? Так? Або інакше, я можу за один раз записати не більше 128 байтів? Чи як? Адресація наскрізна? Ну а якщо більш ніж 128 байт відправити на запис, що відбудеться? Що втратиться? Те що за межами 128 байтів чи перші 128 байт?

Знайшов бібліотеку для eeprom. Спробував - працює. Записав спочатку з 0 адреси 14 байтів, потім з 16 адреси ще 18 байтів і те і те читається. Значить можна в межах однієї сторінки не за раз писати дані? Одного разу один шматочок даних, іншого разу інший шматочок даних? Так? Бо думав що коли пишеш в сторінку, то вона повністю перезаписується і попередні данні втрачаються. Ні?

Ще плутаюсь в термінології. В бібліотеці в файлі eeprom24xxConfig.h

#ifndef    _24XXCONFIG_H
#define    _24XXCONFIG_H

#define    _EEPROM_SIZE_KBIT                            128
#define    _EEPROM24XX_I2C                                hi2c1
#define _EEPROM24XX_ADDRESS                            0x50<<1
#define    _EEPROM_FREERTOS_IS_ENABLE                    0

#endif

Є визначення _EEPROM_SIZE_KBIT як 128. Це правильно для AT24C512? Чи треба вказати таки 512?

Бо крізь по прайсам в магазах мікросхема AT24C512 вказано що вона має пам'яті 512 кілобіт з організацією 64k x 8. Це як? Скільки сторінок по скільки байт? Тоді виходить потрібно _EEPROM_SIZE_KBIT вказати як 64? Я вже нічого не розумію.

Далі в цій бібліотеці в файлі eeprom24xx.c є перевірка розміру записуваних даних:

bool EEPROM24XX_Save(uint16_t Address, void *data, size_t size_of_data)
{
    #if ((_EEPROM_SIZE_KBIT==1) || (_EEPROM_SIZE_KBIT==2))
    if(size_of_data > 8)
    {
        return false;
    }
    #endif
    #if ((_EEPROM_SIZE_KBIT==4) || (_EEPROM_SIZE_KBIT==8) || (_EEPROM_SIZE_KBIT==16))
    if(size_of_data > 16)
    {
        return false;
    }
    #endif
    #if ((_EEPROM_SIZE_KBIT==32) || (_EEPROM_SIZE_KBIT==64) || (_EEPROM_SIZE_KBIT==128))
    if(size_of_data > 32)
    {
        return false;
    }
    #endif
    
    #if ((_EEPROM_SIZE_KBIT==1) || (_EEPROM_SIZE_KBIT==2))
    if(HAL_I2C_Mem_Write(&_EEPROM24XX_I2C, _EEPROM24XX_ADDRESS, Address, I2C_MEMADD_SIZE_8BIT, (uint8_t*) data, size_of_data, 100) == HAL_OK)
    #else
    if(HAL_I2C_Mem_Write(&_EEPROM24XX_I2C, _EEPROM24XX_ADDRESS, Address, I2C_MEMADD_SIZE_16BIT, (uint8_t*) data, size_of_data, 100) == HAL_OK)
    #endif
    {
        #if (_EEPROM_FREERTOS_IS_ENABLE==1)
        osDelay(7);
        #else
        HAL_Delay(7);
        #endif
        return true;
    }
    else
    {
        return false;
    }
}

Максимум тут _EEPROM_SIZE_KBIT==128. І тоді розмір даних не має перевищувати 32 байт. Чому так? Якщо в 24C512 сторінка 128 байт, чому не можна більш ніж 32?
То таки в 24C512 розмір _EEPROM_SIZE_KBIT = 512, чи таки 64? Як 512 то потрібно модифікувати бібліотеку. Так?

Каша в голові. Не можу зрозуміти. Направте в потрібне русло.

2 Востаннє редагувалося ReAl (20.05.2018 16:29:11)

Re: Організація пам'яті EEPROM AT24C512

Сторінкова організація — приблизно як у динамічний пам'яті row/column (row ~ page).
Тобто коли отримується адреса сторінки, вся сторінка зчитується в RAM-буфер (див. рис. 3-1 на стор. 3 документа), а адреса байту всередині сторінки ставить SERIAL MUX на його початок.
Тепер можна читати хоч всю пам'ять підряд, при досягненні останнього байту сторінки (якщо мікроконтролер послав ACK, бо збирається читати далі) у буфер зачитується наступна сторінка.
При записові пишеться у той же буфер починаючи з адреси байту всередині сторінки (тому коли перезаписується сторінка, то нечіпані дані пишуться назад). Запис всього буфера сторінки у EEPROM-накопичувач починається по STOP мікроконтролера. Наступні записи — після закінчення часу запису. Можна дрібними шматками писати в одну сторінку, але це невигідно по ресурсу перезапису.
Якщо під час запису в буфер перейти межу сторінки, то адреса, якщо я правильно пам'ятаю, «згортається» по межі сторінки і зайві байти підуть на початок буфера, потім запишуться у цю ж саму сторінку.
Тобто, якщо стартувати, приміром з середини сторінки і записати 256 байт (при сторінці 128 байт), то в масив EEPROM у першу половину сторінки потраплять останні 64 з цих 256 байтів, у другу половину сторінки — передостанні 64 з цих 256 байтів.

Бібліотечка може просто не враховувати 24c512. Ну, не цікавили автора.

printf("Nested comments is %s\n", */*/**/"*/"/*"/**/ == '*' ? "OFF" : "ON");

3

Re: Організація пам'яті EEPROM AT24C512

_EEPROM_SIZE_KBIT для 24C512 це таки 512
Там треба так:

    #if ((_EEPROM_SIZE_KBIT==32) || (_EEPROM_SIZE_KBIT==64) || (_EEPROM_SIZE_KBIT==128))
    if(size_of_data > 32)
    {
        return false;
    }
    #elif (_EEPROM_SIZE_KBIT==256)
    if(size_of_data > 64)
    {
        return false;
    }
    #elif (_EEPROM_SIZE_KBIT==512)
    if(size_of_data > 128)
    {
        return false;
    }
    #endif

хоча, як на мене, розмір не повинен затовктуватися через #define, це майже нічого (а при використанні LTO з одим-єдиним розміром EEPROM у проекті так швидше за все зовсім нічого) не економить, а гнучкість втрачається.

printf("Nested comments is %s\n", */*/**/"*/"/*"/**/ == '*' ? "OFF" : "ON");
Подякували: taburyak1

4

Re: Організація пам'яті EEPROM AT24C512

p.s. Ну і помилка ж, за великим рахунком, буде не тоді, коли розмір даних перевищує розмір сторінки, а коли пакет переходить межу сторінки, що може трапитися і при записові 2 байтів починаючи з адреси кінця сторінки (навряд чи бажалося перший байт записати в кінець сторінки, а другий на початок тієї самої сторінки).

printf("Nested comments is %s\n", */*/**/"*/"/*"/**/ == '*' ? "OFF" : "ON");
Подякували: taburyak1

5

Re: Організація пам'яті EEPROM AT24C512

ReAl написав:

Бібліотечка може просто не враховувати 24c512. Ну, не цікавили автора.

Велике дякую за всі пояснення. Особливо про процеси які відбуваються при запису в чипові. Тепер розвиднилось мені. Ну і сам не склавши руки сидів. Трішки в спокої почитав, переглянув ще одну бібліотечку по темі. Вона більш зрозуміла і гнучкіша. Буфер у 32 байти зроблений для сумісності. Ну і там по тому як організовано запис читання я зрозумів принцип трішки краще чим у попередній бібліотеці. Перевірив запис читання за допомоги цієї бібліотеки - все працює як слід і зрозуміліше для мене.
Це суперово!
Дяка, дяка, дяка!!!!

6 Востаннє редагувалося ReAl (21.05.2018 11:56:14)

Re: Організація пам'яті EEPROM AT24C512

Шаблони (не в сенсі C++ template, а в сенсі pattern) — класна штука. Їх треба знати і їх треба розпізнавати.
У даному випадку бібліотечка не використовує шаблон «треба зробити щось велике, але робити можна лише обмеженими по розміру порціями», через що код важче читається і гірше супроводжується (дві гілки if-else містять практично ідентичний код, у випадку змін можна ненароком недопоміняти, наприклад, при переході на 24C1024 замінити в одній гілці додати старший біт адреси до EEPROM_ADDRESS, а у іншій забути).
Порівняйте:

HAL_StatusTypeDef
Eeprom::writeEEPROM(uint16_t address, 
                    uint8_t* MemTarget,
                    uint16_t Size)
{
    uint16_t Counter = 0;
    HAL_StatusTypeDef Result = HAL_OK;
    while (Counter < Size && Result == HAL_OK) {
        uint16_t Portion = Size - Counter;          // Все, що залишилося -- у одну порцію
        if (Portion > EEPROM_MAXPKT) Portion = EEPROM_MAXPKT; // Порція завелика, зменшуємо
        // Працюємо із порцією, яку можна проковтнути
        Result = HAL_I2C_Mem_Write(i2c_port, EEPROM_ADDRESS,
                    address + Counter, I2C_MEMADD_SIZE_16BIT,
                    &MemTarget[Counter], Portion, EEPROM_TIMEOUT);
        // І реєструємо, що вже це зробили.
        Counter += Portion;

        HAL_Delay(EEPROM_WRITE);
    }
    return Result;
}

І знову ж таки, тут (без використання «обгортки», яка приймає номер «секції», тобто таки сторінки в EEPROM) ризик дати адресу не з початку сторінки і все піде шкереберть. Першу порцію все ж таки краще обмежувати маскуванням початкової адреси на розмір_сторінки_мінус_один і обчисленням кількості байтів від початкової адреси до кінця сторінки.

printf("Nested comments is %s\n", */*/**/"*/"/*"/**/ == '*' ? "OFF" : "ON");

7

Re: Організація пам'яті EEPROM AT24C512

ReAl написав:

(дві гілки if-else містять практично ідентичний код, у випадку змін можна ненароком недопоміняти, наприклад, при переході на 24C1024 замінити в одній гілці додати старший біт адреси до EEPROM_ADDRESS, а у іншій забути).
Порівняйте:

Тааааак, це я зрозумів. Дякую за слушне зауваження. І за зразок коду.

ReAl написав:

І знову ж таки, тут (без використання «обгортки», яка приймає номер «секції», тобто таки сторінки в EEPROM) ризик дати адресу не з початку сторінки і все піде шкереберть. Першу порцію все ж таки краще обмежувати

А тут зрозумів, що потрібно слідкувати, щоб новий запис починався з початку сторінки, якщо "пакет" перевищує розмір сторінки? Чи в принципі щоб починати запис з початку сторінки завжди?

ReAl написав:

обмежувати маскуванням початкової адреси на розмір_сторінки_мінус_один і обчисленням кількості байтів від початкової адреси до кінця сторінки.

А цю фразу, до свого сорому, я не зрозумів. Як вам буде ласка поясніть іншими словами, чи простіше. Дякую.

8

Re: Організація пам'яті EEPROM AT24C512

taburyak написав:
ReAl написав:

І знову ж таки, тут (без використання «обгортки», яка приймає номер «секції», тобто таки сторінки в EEPROM) ризик дати адресу не з початку сторінки і все піде шкереберть. Першу порцію все ж таки краще обмежувати

А тут зрозумів, що потрібно слідкувати, щоб новий запис починався з початку сторінки, якщо "пакет" перевищує розмір сторінки? Чи в принципі щоб починати запис з початку сторінки завжди?

Потрібно, щоб від довжина пакету не перевищувала відстань від початкової адреси до кінця сторінки, а не розмір сторінки. Реально там взагалі він для якоїсь «сумісності» вдвічі занизив розмір сторінки, але то окрема розмова.
Проблема, на мій погляд, в тому, що «назовні» стирчить функція, яка дозволяє зробити помилку, при тому, що вона начебто контролює аргументи (і створює хибну впевненість у безпечності). Якби назовні було видно лише метод, який дозволяє адресу вказати як номер сторінки (і початок запису завжди з початку сторінки), то перевірка довжини на розмір сторінки була б коректною. Якщо можна вказати адресу будь-де в межах сторінки, то перевірка на розмір сторінки не має сенсу.

Якщо функція всі порції обмежує розміром (цілої) сторінки, то вона повинна приймати як початкову даресу номер (цілої) сторінки. Якщо ж функція приймає аргумент — довільну адресу, у довільномі місці відносно початку сторінки, то перший запис слід робити порцією не в розмір сторінки, а у відстань до кінця сторінки. Інакше це через певний час вилізе боком.
Про маски зараз напишу.

printf("Nested comments is %s\n", */*/**/"*/"/*"/**/ == '*' ? "OFF" : "ON");

9 Востаннє редагувалося ReAl (27.05.2018 17:09:20)

Re: Організація пам'яті EEPROM AT24C512

taburyak написав:
ReAl написав:

обмежувати маскуванням початкової адреси на розмір_сторінки_мінус_один і обчисленням кількості байтів від початкової адреси до кінця сторінки.

А цю фразу, до свого сорому, я не зрозумів. Як вам буде ласка поясніть іншими словами, чи простіше. Дякую.

Розмір сторінки — то степінь двійки. Тобто виражається числом виду 100..02, одиничка і якась кількість нулів. Якщо від того відняти одиницю, то отримаємо 011..12, маску бітів всередині сторінки. Тобто

// Можна define, можна як параметр «драйвера» для даного сеансу
unsigned page_size = 64;                 // наприклад, це 0x40
// Маску обчислюємо по заданому розміру сторінки.
unsigned inpage_addr_mask = page_size-1; // це 0x40-1 = 0x3F

Операції з маскою

//Перевірка, чи адреса від початку сторінки, тобто чи нульова адреса всередині сторінки
bool is_from_page_start(unsigned addr)
{
    return (addr & inpage_addr_mask) == 0;
}

Можна було б addr % page_size, але це досі може бути дорогувато по коду/часу (для stm32 не так, щоб дуже дорого, але навіщо?) — у тому випадкові, якщо page_size змінний параметр, а не забита на етапі компіляції константа/#define.

// обчислення відстані до кінця сторінки
unsigned size_to_page_end(unsigned addr)
{
    return (~addr & inpage_addr_mask) + 1;
}

+1 тут в певному сенсі компенсує -1 при обчисленні маски
(а взагалі дуже рекомендую прочитати Bit Twiddling Haks — там не все ідеально, але дуже пізнавально)

І тепер (тут я написав як звичніше мені — len у циклі зменшується, початкова адреса EEPROM і адреса буфера даних збільшуються).

HAL_StatusTypeDef
Eeprom::writeEEPROM(uint16_t address,
                    const uint8_t * pdata,
                    uint16_t len)
{
    HAL_StatusTypeDef result = HAL_OK;

    // Перша порція точно не може бути більша.
    // І це точно не більше за розмір сторінки.
    uint16_t portion = size_to_page_end(address);
    // Але ми могли захотіти писати ще менше
    if (portion > len) portion = len;

    while (len != 0 && result == HAL_OK) {
        // Працюємо із порцією, яку можна проковтнути
        result = HAL_I2C_Mem_Write(i2c_port, EEPROM_ADDRESS,
                    address, I2C_MEMADD_SIZE_16BIT,
                    pdata, portion, EEPROM_TIMEOUT);
        // І реєструємо, що вже це зробили.
        len     -= portion;
        address += portion;
        pdata   += portion;

        HAL_Delay(EEPROM_WRITE);

        // От тепер нова для нового циклу
        portion = len;                       // Все, що залишилося -- у одну порцію
        if (portion > page_size) portion = page_size; // Порція завелика, зменшуємо
    }

    return result;
}

_______________
Була помилка «недоредагування після копіпасту» :-) у функції bool is_from_page_start(unsigned addr);

printf("Nested comments is %s\n", */*/**/"*/"/*"/**/ == '*' ? "OFF" : "ON");
Подякували: taburyak1

10 Востаннє редагувалося ReAl (26.05.2018 15:31:20)

Re: Організація пам'яті EEPROM AT24C512

Навіть так.
Заразом переробив на unsigned, бо для STM32 використовувати не «рідний» розмір, а фіксований uint16_t розміром у пів слова, є сенс лише коли це принципово. Тут — ні, і навіть не дасть працювати з повним розміром 24C512.

// Для 24C512 треба до EEPROM_ADDRESS при address >= 0x10000 додати 0x1 або 0x2
// залежно від того, зсунуту чи не зсунуту адресу I2C використовує HAL
// Тобто аргумент виклику
//   EEPROM_ADDRESS + ((address & 0x10000) >> 16),
// або 15, якщо адреса I2C вже зсунута вліво
HAL_StatusTypeDef
Eeprom::writeEEPROM(unsigned address,
                    const uint8_t * pdata,
                    unsigned len)
{
    HAL_StatusTypeDef result = HAL_OK;

    // Перша порція не може бути більша, щоб не перетнула розмір сторінки
    unsigned max_portion = size_to_page_end(address);
    unsigned portion;

    while (len != 0 && result == HAL_OK) {
        portion = len;                       // Все, що залишилося -- у одну порцію
        if (portion > max_portion) portion = max_portion;  // Порція завелика, зменшуємо
        // Працюємо із порцією, яку можна проковтнути
        result = HAL_I2C_Mem_Write(i2c_port,
                    EEPROM_ADDRESS, // для 24C512 див коментар
                    address, I2C_MEMADD_SIZE_16BIT,
                    pdata, portion, EEPROM_TIMEOUT);
        // І реєструємо, що вже це зробили.
        len     -= portion;
        address += portion;
        pdata   += portion;
        // наступне, якщо взагалі буде, буде від початку сторінки
        max_portion = page_size;

        HAL_Delay(EEPROM_WRITE);
    }

    return result;
}
printf("Nested comments is %s\n", */*/**/"*/"/*"/**/ == '*' ? "OFF" : "ON");
Подякували: taburyak1

11 Востаннє редагувалося ReAl (26.05.2018 22:42:59)

Re: Організація пам'яті EEPROM AT24C512

І ще навздогін. Зазвичай я у прототипі функції запису роблю вказівник на дані const void *src, щоб можна було підсунути адресу будь-якої структури без приведення типу вказівника у викликові, а тоді вже у самій функції

const uint8_t *pdata = (const uint8_t*)src;

і далі по тексту.

printf("Nested comments is %s\n", */*/**/"*/"/*"/**/ == '*' ? "OFF" : "ON");
Подякували: taburyak1

12

Re: Організація пам'яті EEPROM AT24C512

ReAl дякую за досвід, науку. Дуже корисні зауваження і поради. Завтра розбиратимусь зі всім тим.

13

Re: Організація пам'яті EEPROM AT24C512

taburyak написав:

Завтра розбиратимусь зі всім тим.

Ой, там вище була помилка «недоредагування після копіпасту» :) у функції bool is_from_page_start(unsigned addr);, виправив.

printf("Nested comments is %s\n", */*/**/"*/"/*"/**/ == '*' ? "OFF" : "ON");
Подякували: taburyak1

14 Востаннє редагувалося taburyak (28.05.2018 14:11:11)

Re: Організація пам'яті EEPROM AT24C512

Зробив так.

/*
 * AT24Cxx.h
 *
 *  Created on: 20 трав. 2018 р.
 *      Author: Andriy
 */

#ifndef AT24CXX_H_
#define AT24CXX_H_

#include "stm32f1xx_hal.h"
#include "stdbool.h"

#define    EEPROM_I2C            hi2c1
#define EEPROM_ADDRESS        0x50
//#define EEPROM_MAXPKT        32                    //(page size)
#define EEPROM_PAGESIZE        128
#define EEPROM_WRITE        10                    //time to wait in ms
#define EEPROM_TIMEOUT        5 * EEPROM_WRITE    //timeout while writing

extern I2C_HandleTypeDef EEPROM_I2C;

HAL_StatusTypeDef AT24Cxx_IsConnected(void);
HAL_StatusTypeDef AT24Cxx_ReadEEPROM(unsigned address, const void* src, unsigned len);
HAL_StatusTypeDef AT24Cxx_WriteEEPROM(unsigned address, const void* src, unsigned len);

#endif /* AT24CXX_H_ */

Та отак:

/*
 * AT24Cxx.c
 *
 *  Created on: 20 трав. 2018 р.
 *      Author: Andriy
 */
#include "AT24Cxx.h"

// Перетворюємо адресу з 7 біт на на 8 біт
static unsigned eeprom_address = EEPROM_ADDRESS << 1;
// Маску обчислюємо по заданому розміру сторінки.
static unsigned inpage_addr_mask = EEPROM_PAGESIZE - 1;

static HAL_StatusTypeDef AT24Cxx_WriteReadEEPROM(unsigned address, const void* src, unsigned len, bool write);
static unsigned size_to_page_end(unsigned addr);

HAL_StatusTypeDef AT24Cxx_IsConnected(void)
{
    return HAL_I2C_IsDeviceReady(&EEPROM_I2C, eeprom_address, 1, EEPROM_TIMEOUT);
}

HAL_StatusTypeDef AT24Cxx_ReadEEPROM(unsigned address, const void* src, unsigned len)
{
    return AT24Cxx_WriteReadEEPROM(address, src, len, false);
}

HAL_StatusTypeDef AT24Cxx_WriteEEPROM(unsigned address, const void* src, unsigned len)
{
    return AT24Cxx_WriteReadEEPROM(address, src, len, true);
}

static HAL_StatusTypeDef AT24Cxx_WriteReadEEPROM(unsigned address, const void* src, unsigned len, bool write)
{
    uint8_t *pdata = (uint8_t*) src;

    HAL_StatusTypeDef result = HAL_OK;

    // Перша порція не може бути більша, щоб не перетнула розмір сторінки
    unsigned max_portion = size_to_page_end(address);
    unsigned portion;

    while (len != 0 && result == HAL_OK)
    {
        portion = len;              // Все, що залишилося -- у одну порцію

        if (portion > max_portion)
        {
            portion = max_portion;  // Порція завелика, зменшуємо
        }

        // Працюємо із порцією, яку можна проковтнути
        if(write)
        {
            result = HAL_I2C_Mem_Write(&EEPROM_I2C,
                                    eeprom_address,
                                    address,
                                    I2C_MEMADD_SIZE_16BIT,
                                    pdata,
                                    portion,
                                    EEPROM_TIMEOUT);
        }
        else
        {
            result = HAL_I2C_Mem_Read(&EEPROM_I2C,
                                    eeprom_address,
                                    address,
                                    I2C_MEMADD_SIZE_16BIT,
                                    pdata,
                                    portion,
                                    EEPROM_TIMEOUT);
        }

        // І реєструємо, що вже це зробили.
        len     -= portion;
        address += portion;
        pdata   += portion;

        // наступне, якщо взагалі буде, буде від початку сторінки
        max_portion = EEPROM_PAGESIZE;

        if(write)
        {
            HAL_Delay(EEPROM_WRITE);
        }
        else
        {
            HAL_Delay(EEPROM_WRITE / 2);
        }
    }

    return result;
}

// обчислення відстані до кінця сторінки
static unsigned size_to_page_end(unsigned addr)
{
    return (~addr & inpage_addr_mask) + 1;
}

Так працює:

/* USER CODE BEGIN 2 */

  UART_SendStr("UART is OK!\n\r");

  if(AT24Cxx_IsConnected() == HAL_OK)
  {
      UART_SendStr("EEPROM is OK!\n\r");

      char str1[] = "https://stm32withoutfear.blogspot.com/2018/05/stm32-eeprom-at24xxx-i2c.html";

      if(AT24Cxx_WriteEEPROM(196, str1, sizeof(str1) / sizeof(str1[0])) == HAL_OK)
      {
          UART_SendStr("EEPROM save is OK!\n\r");
      }
      else
      {
          UART_SendStr("EEPROM save is failed!\n\r");
      }

      unsigned var1 = 12345678;

      if(AT24Cxx_WriteEEPROM(60, &var1, sizeof(var1)) == HAL_OK)
      {
          UART_SendStr("EEPROM save is OK!\n\r");
      }
      else
      {
          UART_SendStr("EEPROM save is failed!\n\r");
      }

      char str2[128] = {};
      unsigned var2;
      AT24Cxx_ReadEEPROM(196, str2, 128);
      UART_SendStr(str2);
      UART_SendStr("\n\r");
      AT24Cxx_ReadEEPROM(60, &var2, sizeof(var2));
      UART_SendInt(var2);
      UART_SendStr("\n\r");
  }
  /* USER CODE END 2 */

Оголосив структуру:

typedef struct{
    int intVar;
    char str[256];
    uint8_t flag;
    uint16_t save;
}ExampleTypeDef;

Так не працює, читання не HAL_OK:

/* USER CODE BEGIN 2 */

  UART_SendStr("UART is OK!\n\r");

  if(AT24Cxx_IsConnected() == HAL_OK)
  {
      UART_SendStr("EEPROM is OK!\n\r");
      UART_SendStr("Size data = ");
      UART_SendInt(sizeof(example));
      UART_SendStr("\n\r");
// Читання завершується != HAL_OK.
      if(AT24Cxx_ReadEEPROM(396, &example, sizeof(example)))
      {
          if(example.save != 0xABCD)
          {
              example.flag = 1;
                example.intVar = 12345678;
                example.save = 0xABCD;
                strcpy(example.str, "Testing for save any data structure. Thank you, ReAl!!! The link http://replace.org.ua/topic/9430/");

                if(AT24Cxx_WriteEEPROM(396, &example, sizeof(example)))
              {
                  UART_SendStr("EEPROM save is OK!\n\r");
              }
              else
              {
                  UART_SendStr("EEPROM save is failed!\n\r");
              }
          }

          UART_SendInt(example.intVar);
          UART_SendStr("\n\r");
          UART_SendStr("0x");
          UART_SendHex16(example.save);
          UART_SendStr("\n\r");
          UART_SendStr(example.str);
          UART_SendStr("\n\r");
      }
      else
      {
          UART_SendStr("EEPROM read is failed!\n\r");
      }
  }
  else
  {
      UART_SendStr("EEPROM is not connected!\n\r");
  }
  }
  /* USER CODE END 2 */

Два демо-коди разом в терміналі:
UART is OK!
EEPROM is OK!
Size data = 264
EEPROM read is failed!
EEPROM is OK!
EEPROM save is OK!
EEPROM save is OK!
https://stm32withoutfear.blogspot.com/2 … x-i2c.html
12345678

Що не так зі структурою, чому не хоче читатись?

15

Re: Організація пам'яті EEPROM AT24C512

Це так вийшло при копіюванні сюди, чи це так в коді
Читання:

      // Читання завершується != HAL_OK.
      if(AT24Cxx_ReadEEPROM(396, &example, sizeof(example)))

А запис був:

      if(AT24Cxx_WriteEEPROM(60, &var1, sizeof(var1)) == HAL_OK)

При зчитуванні нема перевірки на рівність HAL_OK, а він сам рівний 0, все, що не 0, то різні коди помилок. В результаті коли все добре і AT24Cxx_ReadEEPROM повертає HAL_OK, if провалюється на else.

По дорозі:

      if(AT24Cxx_WriteEEPROM(196, str1, sizeof(str1) / sizeof(str1[0])) == HAL_OK)

Не треба тут ділити на sizeof елемента масиву, бо тут потрібна саме кількість байтів, а не кількість елементів. Просто пощастило, що sizeof(str1[0]) дорівнює 1.

printf("Nested comments is %s\n", */*/**/"*/"/*"/**/ == '*' ? "OFF" : "ON");
Подякували: taburyak1

16 Востаннє редагувалося ReAl (29.05.2018 00:11:17)

Re: Організація пам'яті EEPROM AT24C512

p.s. Насправді з sizeof все ще хитріше, бо він повертає не в байтах, а в char-ах, а є архітектури, де гранулярність пам'яті 16 біт, тобто мінімальний об'єкт саме в пам'яті два байти, там  CHAR_BIT == 16, тобто буде sizeof(uint16_t) = sizeof(char) = 1. Як воно тоді працює з масивами uint8_t я не знаю, не працював з ними.

Може в пам'яті зберігати по два uint8_t в одній комірці (але тоді при роботі з масивом треба розпаковувати-запаковувати), тоді власне байтів буде вдвічі більше за sizeof(array). І при роботі з контролером I2C те ж саме треба буде робити (робитиме HAL). Але навряд чи так, бо для будь-якого об'єкта має бути своя адреса в пам'яті, а у таких двох в одній комірці адреса буде лише одна.

Може зберігати по одному uint8_t в комірці пам'яті, тоді робота швидка (треба лише маскувати 0-ми старші 8 бітів, щоб поведінка 255+1 → 0 була правильною) і власне байтів буде стільки ж, скільки sizeof(array). Будуть зайві витрати пам'яті.

В світі багато цікавого, але от самі ті кристали я так і не помацав, нема для них задачі.

А поки все, з чим працюю, має гранулярність адресації пам'яті 8 біт, sizeof дає кількість в одиницях uint8_t, і я сам всім таким не заморочуюсь.

printf("Nested comments is %s\n", */*/**/"*/"/*"/**/ == '*' ? "OFF" : "ON");

17

Re: Організація пам'яті EEPROM AT24C512

ReAl написав:

Це так вийшло при копіюванні сюди, чи це так в коді

Зрозумів. Таки так, треба додати умову == HAL_OK. Вже мабуть зашився зовсім, що елементарної помилки не побачив.
З іншим теж зрозумів. Велика дяка.

18

Re: Організація пам'яті EEPROM AT24C512

Зробив запис в бложик по темі.

Подякували: ReAl1