Кто любит RISC в жизни, заходим, не стесняемся.
Ответить

STM32F0 I2C EEPROM

Вт апр 28, 2015 18:31:17

Всем привет!

Пытаюсь работать с I2C EEPROM при помощи STM32F0. Почти получилось. Чтение работает без проблем (вроде как), а вот запись не работает. Причем так: если пройтись отладчиком - записывается, а если просто запустить - нет.

Код ниже, заранее спасибо!


Определения.
Код:
/* Macros */
#define SET_BIT(REG, BIT)                       ((REG) |= (BIT))
#define CLEAR_BIT(REG, BIT)                     ((REG) &= ~(BIT))
#define READ_BIT(REG, BIT)                      ((REG) & (BIT))

#define SET_REG(REG, VAL)                       ((REG) = (VAL))
#define CLEAR_REG(REG)                          ((REG) = (0))
#define READ_REG(REG)                           ((REG))

#define MODIFY_REG(REG, CLEARMASK, SETMASK)     SET_REG((REG), (((READ_REG(REG)) & (~(CLEARMASK))) | (SETMASK)))
/* Macros */

#define EEPROM_PAGE_SIZE 16


Функции.
Код:
void I2C_EEPROM_Read(void);
uint32_t I2C_EEPROM_Write(void);
uint32_t I2C_EEPROM_WritePage(uint32_t argAddress, uint8_t *argData, uint32_t argDataSize);


Инициализация I2C.
Код:
    /* I2C Initialization */
    SET_BIT(RCC->APB1ENR, RCC_APB1ENR_I2C1EN);
    SET_BIT(RCC->CFGR3, RCC_CFGR3_I2C1SW);
    MODIFY_REG(I2C1->TIMINGR,
        I2C_TIMINGR_PRESC | I2C_TIMINGR_SCLDEL | I2C_TIMINGR_SDADEL | I2C_TIMINGR_SCLH | I2C_TIMINGR_SCLL,
        (1 << 28) | (8 << 20) | (0 << 16) | (94 << 8) | (137 << 0)); /* 0x10805E89 */
    /* I2C Initialization */


Работа с EEPROM.
Код:
    /* EEPROM */
    I2C_EEPROM_Read();

    if(globalData.firstRun != 13)
    {
        SB_Init();
        while(I2C_EEPROM_Write() != 0);
    }
    /* EEPROM */


Чтение из EEPROM.
Код:
void I2C_EEPROM_Read(void)
{
    uint8_t localDataBuf[sizeof(EEPROM_Data)];
    uint32_t localCounter;


    SET_BIT(I2C1->CR1, I2C_CR1_PE);

    SET_REG(I2C1->CR2, (1 << 16) | I2C_CR2_START | 0xA0);
    while((I2C1->ISR & I2C_ISR_TXIS) != I2C_ISR_TXIS);
    SET_REG(I2C1->TXDR, 0x00);
    while((I2C1->ISR & I2C_ISR_TC) != I2C_ISR_TC);

    SET_REG(I2C1->CR2, I2C_CR2_AUTOEND | (sizeof(EEPROM_Data) << 16) | I2C_CR2_START | I2C_CR2_RD_WRN | 0xA0);
    for(localCounter = 0; localCounter < sizeof(EEPROM_Data); localCounter++)
    {
        while((I2C1->ISR & I2C_ISR_RXNE) != I2C_ISR_RXNE);
        localDataBuf[localCounter] = (uint8_t)I2C1->RXDR;
    }
   
    CLEAR_BIT(I2C1->CR1, I2C_CR1_PE);

    memcpy(&globalData, localDataBuf, sizeof(EEPROM_Data));
}


Запись в EEPROM.
Код:
uint32_t I2C_EEPROM_Write(void)
{
    uint8_t localDataBuf[sizeof(EEPROM_Data)];
    uint32_t localCounter;
    uint32_t localLast;
    uint32_t localOffset;
   
   
    memcpy(localDataBuf, &globalData, sizeof(EEPROM_Data));
   
    for(localCounter = 0; localCounter < (sizeof(EEPROM_Data) / EEPROM_PAGE_SIZE); localCounter++)
    {
        localOffset = localCounter * EEPROM_PAGE_SIZE;
        if(I2C_EEPROM_WritePage(localOffset, &localDataBuf[localOffset], EEPROM_PAGE_SIZE) != 0)
        {
            return 1;
        }
    }
       
    localLast = sizeof(EEPROM_Data) % EEPROM_PAGE_SIZE;
    if(localLast > 0)
    {
        localOffset += EEPROM_PAGE_SIZE;
        if(I2C_EEPROM_WritePage(localOffset, &localDataBuf[localOffset], localLast) != 0)
        {
            return 1;
        }
    }
       
    return 0;
}


Запись страницы (максимум 16 байт).
Код:
uint32_t I2C_EEPROM_WritePage(uint32_t argAddress, uint8_t *argData, uint32_t argDataSize)
{
    uint32_t localCounter;


    SET_BIT(I2C1->CR1, I2C_CR1_PE);

    SET_REG(I2C1->CR2, I2C_CR2_RELOAD | (1 << 16) | I2C_CR2_START | 0xA0);
    while((I2C1->ISR & I2C_ISR_TXIS) != I2C_ISR_TXIS);
    SET_REG(I2C1->TXDR, argAddress);
    while((I2C1->ISR & I2C_ISR_TCR) != I2C_ISR_TCR);

    SET_REG(I2C1->CR2, I2C_CR2_AUTOEND | (argDataSize << 16) | 0xA0);
    for(localCounter = 0; localCounter < argDataSize; localCounter++)
    {
        if((I2C1->ISR & I2C_ISR_NACKF) == I2C_ISR_NACKF)
        {
            CLEAR_BIT(I2C1->CR1, I2C_CR1_PE);
            return 1;
        }
        while((I2C1->ISR & I2C_ISR_TXIS) != I2C_ISR_TXIS);
        I2C1->TXDR = (uint32_t)argData[localCounter];
    }
   
    CLEAR_BIT(I2C1->CR1, I2C_CR1_PE);

    return 0;
}

Re: STM32F0 I2C EEPROM

Пт май 01, 2015 06:11:42

Если под отладчиком работает, а при обычном прогоне нет, то нужно вставить где нибудь паузу методом научного тыка.
Обычно после команды записи байта/страницы нужна пауза.

Re: STM32F0 I2C EEPROM

Ср май 06, 2015 15:59:22

Купил логический анализатор. Нашел проблемы. Исправил. Делюсь рабочим кодом. Если что - задавайте вопросы и вносите поправки! :beer: Без злоебучего SPL! :))

Инициализация

Код:
void I2C_Initialization(void)
{
    RCC->AHBENR |= RCC_AHBENR_GPIOBEN;
    GPIOB->MODER |= GPIO_MODER_MODER9_1 | GPIO_MODER_MODER8_1;
    GPIOB->OTYPER |= GPIO_OTYPER_OT_9 | GPIO_OTYPER_OT_8;
    GPIOB->AFR[1] |= (1 << (1 * 4)) | (1 << (0 * 4));
    RCC->APB1ENR |= RCC_APB1ENR_I2C1EN;
    RCC->CFGR3 |= RCC_CFGR3_I2C1SW;
    I2C1->TIMINGR = 0x10805E89;
    I2C1->CR1 |= I2C_CR1_PE;
}


Чтение

Код:
void I2C_EEPROMRead(void)
{
    uint8_t localDataBuf[sizeof(EEPROM_Data)];
    uint32_t localCounter;

    I2C1->CR2 = (1 << 16) | I2C_CR2_START | 0xA0;
    while((I2C1->ISR & I2C_ISR_TXIS) != I2C_ISR_TXIS);
    I2C1->TXDR = 0;
    while((I2C1->ISR & I2C_ISR_TC) != I2C_ISR_TC);
    I2C1->CR2 = I2C_CR2_AUTOEND | (sizeof(EEPROM_Data) << 16) | I2C_CR2_START | I2C_CR2_RD_WRN | 0xA0;
    for(localCounter = 0; localCounter < sizeof(EEPROM_Data); localCounter++)
    {
        while((I2C1->ISR & I2C_ISR_RXNE) != I2C_ISR_RXNE);
        localDataBuf[localCounter] = (uint8_t)I2C1->RXDR;
    }
    SYSTICK_DelayMs(5);
    memcpy(&globalData, localDataBuf, sizeof(EEPROM_Data));
}



Запись

Код:
void I2C_EEPROMWrite(void)
{
    uint8_t localDataBuf[sizeof(EEPROM_Data)];
    uint32_t localCounter1;
    uint32_t localCounter2;
    uint32_t localPtr;

    memcpy(localDataBuf, &globalData, sizeof(EEPROM_Data));
    localPtr = 0;
    for(localCounter2 = 0; localCounter2 < (sizeof(EEPROM_Data) / EEPROM_PAGE_SIZE); localCounter2++)
    {
        I2C1->CR2 = I2C_CR2_RELOAD | (1 << 16) | I2C_CR2_START | 0xA0;
        while((I2C1->ISR & I2C_ISR_TXIS) != I2C_ISR_TXIS);
        I2C1->TXDR = localPtr;
        while((I2C1->ISR & I2C_ISR_TCR) != I2C_ISR_TCR);
        I2C1->CR2 = I2C_CR2_AUTOEND | (EEPROM_PAGE_SIZE << 16) | 0xA0;
        for(localCounter1 = 0; localCounter1 < EEPROM_PAGE_SIZE; localCounter1++)
        {
            while((I2C1->ISR & I2C_ISR_TXIS) != I2C_ISR_TXIS);
            I2C1->TXDR = (uint32_t)localDataBuf[localPtr];
            localPtr++;
        }
        SYSTICK_DelayMs(5);
    }
    if(sizeof(EEPROM_Data) % EEPROM_PAGE_SIZE)
    {
        I2C1->CR2 = I2C_CR2_RELOAD | (1 << 16) | I2C_CR2_START | 0xA0;
        while((I2C1->ISR & I2C_ISR_TXIS) != I2C_ISR_TXIS);
        I2C1->TXDR = localPtr;
        while((I2C1->ISR & I2C_ISR_TCR) != I2C_ISR_TCR);
        I2C1->CR2 = I2C_CR2_AUTOEND | ((sizeof(EEPROM_Data) % EEPROM_PAGE_SIZE) << 16) | 0xA0;
        for(localCounter1 = 0; localCounter1 < (sizeof(EEPROM_Data) % EEPROM_PAGE_SIZE); localCounter1++)
        {
            while((I2C1->ISR & I2C_ISR_TXIS) != I2C_ISR_TXIS);
            I2C1->TXDR = (uint32_t)localDataBuf[localPtr];
            localPtr++;
        }
        SYSTICK_DelayMs(5);
    }
}

Re: STM32F0 I2C EEPROM

Ср май 06, 2015 19:58:53

Про EEPROM.
- Страница начинается только с адреса кратного размеру страницы.
- Цикл записи начинается по STOP условию.
- Запись длится около 3 мс, отслеживать состояние готовности после записи лучше постоянно опрашивая EEPROM: START+адрес, пока идет запись не будет ACK'а.

Re: STM32F0 I2C EEPROM

Сб июн 01, 2019 13:36:55

В надежде на то, что библиотеки облегчают жизнь, два дня мучился для написания кода чтения и записи EEProm памяти с помощью библиотеки LL (Low layer libraries).
Не помогло. Пришлось читать документацию и запоминать какой бит какого регистра за что отвечает. Нафига тогда библиотека, если она не облегчает написание кода?
Даже примеров толком нет.

Короче, все таки набил код для записи, а потом чтения памяти. Без прерываний, просто, чтобы кто-то потратил на 2 дня меньше времени на освоение этой дури.

Код:
MX_I2C1_Init();

После базовой инициализации:
Код:
   uint8_t slave_address = 0xA0;   // Адрес ведомого устройства в шине I2C (микросхема памяти EEprom)
   uint16_t write_address = 0x0007; // Адрес в памяти EEprom, по которому будем записывать байт
   uint8_t wr_byte = 0xA4; // Записывать будем такой байт

   /* Запись байта.
    * Номер I2C, по которому передавать данные, адрес ведомого устройства, к которому обращаемся, режим адресации 7 бит,
    * количество передаваемых байт 3, после завершения передачи сгенерировать СТОП-сигнал,
    * перед началом сгенерировать СТАРТ-сигнал с признаком записи байта
    */
   LL_I2C_HandleTransfer(I2C1, slave_address , LL_I2C_ADDRSLAVE_7BIT, 3, LL_I2C_MODE_AUTOEND, LL_I2C_GENERATE_START_WRITE);
   while(!(LL_I2C_IsActiveFlag_TXE(I2C1)));
   LL_I2C_TransmitData8(I2C1, (uint8_t)(write_address >> 16));
   while(!(LL_I2C_IsActiveFlag_TXE(I2C1)));
   LL_I2C_TransmitData8(I2C1, (uint8_t)(write_address));
   while(!(LL_I2C_IsActiveFlag_TXE(I2C1)));
   LL_I2C_TransmitData8(I2C1, wr_byte);
   // Передача закончилась, автоматически сгенерирован СТОП-сигнал

   HAL_Delay(3); // Ждем 3 мс, пока микросхема памяти прожует полученное.


   uint16_t read_address = 0x0000; // Адрес в памяти EEprom, с которого начнем считывать байты (начнем с нуля)

   /* Чтения байтов начиная с заданного адреса.
    * Номер I2C, по которому передавать данные, адрес ведомого устройства, к которому обращаемся, режим адресации 7 бит,
    * количество передаваемых байт 3, после завершения передачи НЕ генерировать СТОП-сигнал,
    * перед началом сгенерировать СТАРТ-сигнал с признаком записи байта (ДА, ЗАПИСИ, сначала отправляем адрес в памяти,
    * с которого хотим начать чтение)
    */
   LL_I2C_HandleTransfer(I2C1, slave_address, LL_I2C_ADDRSLAVE_7BIT, 2, LL_I2C_MODE_SOFTEND, LL_I2C_GENERATE_START_WRITE);
   while(!(LL_I2C_IsActiveFlag_TXE(I2C1)));   // Флаг означает, что Выходной буфер свободен и можно записывать следующее значение
   LL_I2C_TransmitData8(I2C1, (uint8_t)(read_address >> 16));
   while(!(LL_I2C_IsActiveFlag_TXE(I2C1)));
   LL_I2C_TransmitData8(I2C1, (uint8_t)(read_address));

   while(!(LL_I2C_IsActiveFlag_TC(I2C1)));      // Флаг означает, что все (два) байта переданы
   // Передача закончилась, но СТОП-сигнал не генерируется (такой формат общения с памятью)

   /*
    * Генерируем СТАРТ-сингал с признаком чтения байта. Будем принимать 13 байт, после окончания приема сгенерировать
    * СТОП-сигнал.
    */
   LL_I2C_HandleTransfer(I2C1, slave_address, LL_I2C_ADDRSLAVE_7BIT, 13, LL_I2C_MODE_AUTOEND, LL_I2C_GENERATE_START_READ);

   uint8_t i;
   uint8_t receive_buf[32] = {0};   // Массив для записи принятых байт

   for(i=0; i<13; i++){
      while(!(LL_I2C_IsActiveFlag_RXNE(I2C1)));   // Флаг означает, что в Приемном буфере появились данные
      receive_buf[i] = LL_I2C_ReceiveData8(I2C1);   // Списываем байт из Приемного буфера
   }
   // Все байты приняты, автоматически генерируется СТОП-сигнал

Как выглядит Запись в анализаторе:
Изображение

Как выглядит чтение 13 байт начиная с адреса 0x0000:
Изображение

Re: STM32F0 I2C EEPROM

Вс июн 09, 2019 20:31:04

Странные вы ребята, на HAL есть встроенные функции специально для чтения записи ЕЕПРОМ - работают без проблем.

Re: STM32F0 I2C EEPROM

Чт авг 20, 2020 03:44:59

Странные вы ребята, на HAL есть встроенные функции специально для чтения записи ЕЕПРОМ - работают без проблем.


При использовании HAL например на STM32F030F4 с 16 кб, оный сожрет 2\3 флэша, после чего вы откроете референс мануал, обматерите HAL LL SPL, и сделаете все руками. При этом скажите большое спасибо человеку выложившему свои наработки!

Re: STM32F0 I2C EEPROM

Пт ноя 27, 2020 03:11:23

Спасибо :)

Re: STM32F0 I2C EEPROM

Пт ноя 27, 2020 21:15:17

Ironium писал(а):например на STM32F030F4 с 16 кб, оный сожрет 2\3 флэша, после чего вы откроете референс мануал, обматерите HAL LL SPL
Видимо нужно научится правильно собирать проект и оптимально выбирать опции сборки потому что вы не правы.
Результат компиляции Blink с HAL
Код:
Program size (bytes):    1356
Data size    (bytes):       4
BSS size     (bytes):       4

Total size   (bytes):    1364   (R/W Memory: 8)

=== Сборка закончена: 0 errors, 0 warnings (0 minutes, 3 seconds) ===
Тоже с SPL.
Код:
Program size (bytes):     676
Data size    (bytes):       0
BSS size     (bytes):       0

Total size   (bytes):     676   (R/W Memory: 36)

=== Сборка закончена: 0 errors, 0 warnings (0 minutes, 2 seconds) ===
Где вы увидели 2\3 флеша?
Причем это не "пустой" код, а Blink в состав которого входит код инициализации в том числе настройка тактирования.

Re: STM32F0 I2C EEPROM

Вт июл 20, 2021 08:12:38

>>Видимо нужно научится правильно собирать проект и оптимально выбирать опции сборки потому что вы не правы.
Результат компиляции Blink с HAL

Подскажите, с какими настройками Вы собираете проект?

Re: STM32F0 I2C EEPROM

Вт июл 20, 2021 12:51:39

steklobiz писал(а):с какими настройками Вы собираете проект?
Из файла проекта
Код:
            <Compiler>
               <Add option="-fdata-sections" />
               <Add option="-ffunction-sections" />
               <Add option="-O2" />
               <Add option="-g2" />
            </Compiler>
            <Linker>
               <Add option="-Wl,--gc-sections" />
               <Add option="-flto" />
            </Linker>

Re: STM32F0 I2C EEPROM

Вт июл 20, 2021 12:57:45

С cmsis я ничего не настраивал в проекте, I2C заработал, пишет и читает во все и отовсюду (до чего руки дотянулись) и лишнего не жрет. Какой смысл делать себе нервы халом?

Re: STM32F0 I2C EEPROM

Вт июл 20, 2021 14:00:07

Из файла проекта

Спасибо за ответ. Подскажите, в какой IDE Вы работаете? Я пишу в CubeIDE и не смог найти такой файл

Re: STM32F0 I2C EEPROM

Вт июл 20, 2021 23:17:09

EmBitz.

Re: STM32F0 I2C EEPROM

Ср июл 21, 2021 07:31:25

steklobiz писал(а):Я пишу в CubeIDE и не смог найти такой файл

Зачем тебе этот файл вообще искать? Мурик показал тебе настройки компилятора. Зайди у себя в настройки и вбей их.

Re: STM32F0 I2C EEPROM

Пн авг 02, 2021 18:18:57

Короче, все таки набил код для записи, а потом чтения памяти. Без прерываний, просто, чтобы кто-то потратил на 2 дня меньше времени на освоение этой дури.


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

Моя версия фрагмента кода, передающего адрес
Код:
    //Transfer byte 1 of memory address
    LL_I2C_TransmitData8(I2C1, ((Reg & 0xFF00) >> 8));
    while(!LL_I2C_IsActiveFlag_TXE(I2C1))
    {
    }
    //Transfer byte 0 of memory address
    LL_I2C_TransmitData8(I2C1, (Reg & 0x00FF));
    while(!LL_I2C_IsActiveFlag_TXE(I2C1))
    {
    }
Ответить