STM32F0 I2C EEPROM

Кто любит RISC в жизни, заходим, не стесняемся.
Ответить
kapitan0v
Открыл глаза
Сообщения: 56
Зарегистрирован: Сб июн 14, 2014 09:05:06

STM32F0 I2C EEPROM

Сообщение kapitan0v »

Всем привет!

Пытаюсь работать с 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;
}
Реклама
misyachniy
Прорезались зубы
Сообщения: 219
Зарегистрирован: Вт июл 02, 2013 09:17:49

Re: STM32F0 I2C EEPROM

Сообщение misyachniy »

Если под отладчиком работает, а при обычном прогоне нет, то нужно вставить где нибудь паузу методом научного тыка.
Обычно после команды записи байта/страницы нужна пауза.
Реклама
kapitan0v
Открыл глаза
Сообщения: 56
Зарегистрирован: Сб июн 14, 2014 09:05:06

Re: STM32F0 I2C EEPROM

Сообщение kapitan0v »

Купил логический анализатор. Нашел проблемы. Исправил. Делюсь рабочим кодом. Если что - задавайте вопросы и вносите поправки! :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);
    }
}
BorisSPB
Встал на лапы
Сообщения: 145
Зарегистрирован: Ср фев 01, 2012 10:55:53

Re: STM32F0 I2C EEPROM

Сообщение BorisSPB »

Про EEPROM.
- Страница начинается только с адреса кратного размеру страницы.
- Цикл записи начинается по STOP условию.
- Запись длится около 3 мс, отслеживать состояние готовности после записи лучше постоянно опрашивая EEPROM: START+адрес, пока идет запись не будет ACK'а.
Реклама
Эиком - электронные компоненты и радиодетали
Аватара пользователя
Солнцеворот
Встал на лапы
Сообщения: 141
Зарегистрирован: Вт июн 03, 2008 01:39:47

Re: STM32F0 I2C EEPROM

Сообщение Солнцеворот »

В надежде на то, что библиотеки облегчают жизнь, два дня мучился для написания кода чтения и записи 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:
Изображение
Реклама
Аватара пользователя
tar
Это не хвост, это антенна
Сообщения: 1363
Зарегистрирован: Ср май 26, 2010 18:53:57

Re: STM32F0 I2C EEPROM

Сообщение tar »

Странные вы ребята, на HAL есть встроенные функции специально для чтения записи ЕЕПРОМ - работают без проблем.
[AMS]
Реклама
Аватара пользователя
Ironium
Родился
Сообщения: 11
Зарегистрирован: Вт июл 14, 2015 06:33:29

Re: STM32F0 I2C EEPROM

Сообщение Ironium »

[uquote="tar",url="/forum/viewtopic.php?p=3648262#p3648262"]Странные вы ребята, на HAL есть встроенные функции специально для чтения записи ЕЕПРОМ - работают без проблем.[/uquote]

При использовании HAL например на STM32F030F4 с 16 кб, оный сожрет 2\3 флэша, после чего вы откроете референс мануал, обматерите HAL LL SPL, и сделаете все руками. При этом скажите большое спасибо человеку выложившему свои наработки!
Аватара пользователя
Asmodey
Друг Кота
Сообщения: 6227
Зарегистрирован: Сб янв 28, 2006 22:47:24

Re: STM32F0 I2C EEPROM

Сообщение Asmodey »

Спасибо :)
Астролябия-сама меряет, было бы что мерять!!!
Аватара пользователя
Мурик
Друг Кота
Сообщения: 3383
Зарегистрирован: Пн окт 11, 2010 19:00:08

Re: STM32F0 I2C EEPROM

Сообщение Мурик »

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 в состав которого входит код инициализации в том числе настройка тактирования.
steklobiz
Родился
Сообщения: 3
Зарегистрирован: Вт июл 20, 2021 08:07:13

Re: STM32F0 I2C EEPROM

Сообщение steklobiz »

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

Подскажите, с какими настройками Вы собираете проект?
Аватара пользователя
Мурик
Друг Кота
Сообщения: 3383
Зарегистрирован: Пн окт 11, 2010 19:00:08

Re: STM32F0 I2C EEPROM

Сообщение Мурик »

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>
Аватара пользователя
Asmodey
Друг Кота
Сообщения: 6227
Зарегистрирован: Сб янв 28, 2006 22:47:24

Re: STM32F0 I2C EEPROM

Сообщение Asmodey »

С cmsis я ничего не настраивал в проекте, I2C заработал, пишет и читает во все и отовсюду (до чего руки дотянулись) и лишнего не жрет. Какой смысл делать себе нервы халом?
Астролябия-сама меряет, было бы что мерять!!!
steklobiz
Родился
Сообщения: 3
Зарегистрирован: Вт июл 20, 2021 08:07:13

Re: STM32F0 I2C EEPROM

Сообщение steklobiz »

Из файла проекта
Спасибо за ответ. Подскажите, в какой IDE Вы работаете? Я пишу в CubeIDE и не смог найти такой файл
Аватара пользователя
Мурик
Друг Кота
Сообщения: 3383
Зарегистрирован: Пн окт 11, 2010 19:00:08

Re: STM32F0 I2C EEPROM

Сообщение Мурик »

EmBitz.
tonyk
Это не хвост, это антенна
Сообщения: 1309
Зарегистрирован: Вт ноя 19, 2019 06:10:18

Re: STM32F0 I2C EEPROM

Сообщение tonyk »

steklobiz писал(а):Я пишу в CubeIDE и не смог найти такой файл
Зачем тебе этот файл вообще искать? Мурик показал тебе настройки компилятора. Зайди у себя в настройки и вбей их.
steklobiz
Родился
Сообщения: 3
Зарегистрирован: Вт июл 20, 2021 08:07:13

Re: STM32F0 I2C EEPROM

Сообщение steklobiz »

Короче, все таки набил код для записи, а потом чтения памяти. Без прерываний, просто, чтобы кто-то потратил на 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))
    {
    }
Ответить

Вернуться в «ARM»