STM32F0x работа с EEPROM 24CXX по I2C

STM32F0x работа с EEPROM 24CXX по I2C

Пример чтения/записи EEPROM AT24C01A с помощью микроконтроллера STM32F030C8T6.

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

Мне нужно было хранить порядка 10 байт настроек, для этого я взял то что было под рукой, а именно AT24C01A. Данный EEPROM имеет 1 кБит памяти, то есть 128 байт. Обращение происходит по протоколу I2C (2-wire).

На одну линию I2C можно подключать несколько устройств, каждое устройство должно иметь свой адрес. Что бы подключить несколько EEPROM на одну линию, можно использовать выводы  A0-A2 (пины 1-3) для задания адреса. 

На картинки изображен байт адреса устройства. Для AT24C01A это верхний байт.

AT24C01A address

Прижимая пин к земле, мы устанавливаем соотвествующий бит в 0, а прижимая к питанию, в 1.

То есть прижав все пины A0-A2 к земле адрес будет 0b1010000[r/w]. Если не понятно что такое R/W бит, то прочтите описание протокола I2C, этой информации полно. Вкратце, адрес устройства имеет 7 бит + 1 бит для указания что мы делаем читаем или пишем.

Вывод WP (пин 7) - защита от записи. Если на него подать напряжение питания, то в устройство нельзя будет писать. Если подать землю - писать можно. Я подключил данный пин к МК, но он всегда прижат к 0, так как мне не нужна защита от записи. Можно просто было припаять пин на землю.

Подключение AT24C01A к STM32F030C8T6

Для подключения я выбрал I2C2 МК STM32F030C8T6.

Вывод SCL (пин 6) нашего EEPROM подключен к PB10 МК. А вывод SDA (пин 5) к выводу PB11 МК.

WP (пин 7) подключен к PB2, но в моем случае, как я уже писал выше, он всегда прижат к земле.

Выводы A0-A2 я припаял к земле, соответственно адрес устройства 0b1010000x.

Дальше код с комментариями.

// Адрес зависит от пинов A0-A2
#define EEPROM_ADDR 0xA0
#define EEPROM_I2C I2C2

#define EEPROM_WP_PORT GPIOB
#define EEPROM_WP_PIN GPIO_Pin_2

#define EEPROM_I2C_PORT GPIOB
#define EEPROM_I2C_SCL GPIO_PinSource10
#define EEPROM_I2C_SDA GPIO_PinSource11
#define EEPROM_I2C_SCL_PIN GPIO_Pin_10
#define EEPROM_I2C_SDA_PIN GPIO_Pin_11

void EEPROMInit(void) {
  // Включаем тактирование I2C2
  RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C2, ENABLE);

  GPIO_InitTypeDef port;
  GPIO_StructInit(&port);

  // порты I2C настраиваем как альтернативная функция
  port.GPIO_Mode = GPIO_Mode_AF;
  port.GPIO_Pin = EEPROM_I2C_SCL_PIN | EEPROM_I2C_SDA_PIN;
  port.GPIO_Speed = GPIO_Speed_50MHz;
  // Открытый сток
  port.GPIO_OType = GPIO_OType_OD;
  // Подтяжку внешнюю я не прицепил, хотя предпочитаю внешнюю
  // меньше проблем с выгоранием внутренней подтяжки
  port.GPIO_PuPd = GPIO_PuPd_UP;

  // I2C по ДШ это AF 1
  GPIO_PinAFConfig(EEPROM_I2C_PORT, EEPROM_I2C_SCL, GPIO_AF_1);
  GPIO_PinAFConfig(EEPROM_I2C_PORT, EEPROM_I2C_SDA, GPIO_AF_1);

  GPIO_Init(EEPROM_I2C_PORT, &port);

  I2C_InitTypeDef i2c;
  I2C_StructInit(&i2c);
  I2C_DeInit(EEPROM_I2C);

  i2c.I2C_AnalogFilter = I2C_AnalogFilter_Enable;
  i2c.I2C_DigitalFilter = 0;
  i2c.I2C_Mode = I2C_Mode_I2C;
  // это адрес МК, но так как мы в режиме мастер, он не нужен
  i2c.I2C_OwnAddress1 = 0x00;
  i2c.I2C_Ack = I2C_Ack_Enable;
  i2c.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;
  // настройка на 100кГц при 48мГц, получено с помощью куба.
  i2c.I2C_Timing = 0x20303E5D;

  I2C_Init(EEPROM_I2C, &i2c);
  I2C_Cmd(EEPROM_I2C, ENABLE);
}

// функция записи в EEPROM
// addr - номер байта в EEPROM
// buf это наш байт
uint8_t EEPROMWrite(uint8_t addr, uint8_t buf) {
  uint16_t _t = 0;
  //ждем пока I2C занят
  I2C_WAIT_FLAG(EEPROM_I2C, I2C_FLAG_BUSY, SET, ERROR);

  // команда старт записи, режим перезапуска, шлем 1 байт
  I2C_TransferHandling(EEPROM_I2C, EEPROM_ADDR, 1, I2C_Reload_Mode, I2C_Generate_Start_Write);
  I2C_WAIT_FLAG(EEPROM_I2C, I2C_ISR_TXIS, RESET, ERROR);

  // шлем номер байта который будем писать
  I2C_SendData(EEPROM_I2C, addr);
  // ждем когда отправится байт
  I2C_WAIT_FLAG(EEPROM_I2C, I2C_ISR_TCR, RESET, ERROR);

  // 1 байт данных, автозавершение после отправки байта
  I2C_TransferHandling(EEPROM_I2C, EEPROM_ADDR, 1, I2C_AutoEnd_Mode, I2C_No_StartStop);
  I2C_WAIT_FLAG(EEPROM_I2C, I2C_ISR_TXIS, RESET, ERROR);

  // ждем, если регистр отправки не пуст
  I2C_WAIT_FLAG(EEPROM_I2C, I2C_ISR_TXE, RESET, ERROR);
  // шлем байт
  I2C_SendData(EEPROM_I2C, buf);

  // ждем флага стоп
  I2C_WAIT_FLAG(EEPROM_I2C, I2C_ISR_STOPF, RESET, ERROR);

  // очищаем его
  I2C_ClearFlag(EEPROM_I2C, I2C_ICR_STOPCF);

  return SUCCESS;
}

// функция чтения из EEPROM
// addr - номер байта в EEPROM
// b - куда читаем байт
uint8_t EEPROMRead(uint8_t addr, uint8_t* b) {
  uint16_t _t = 0;
  I2C_WAIT_FLAG(EEPROM_I2C, I2C_FLAG_BUSY, SET, ERROR);

  I2C_TransferHandling(EEPROM_I2C, EEPROM_ADDR, 1, I2C_SoftEnd_Mode, I2C_Generate_Start_Write);
  I2C_WAIT_FLAG(EEPROM_I2C, I2C_ISR_TXIS, RESET, ERROR);

  // шлем номер байта который хотим прочесть
  I2C_SendData(EEPROM_I2C, addr);
  I2C_WAIT_FLAG(EEPROM_I2C, I2C_ISR_TC, RESET, ERROR);

  I2C_TransferHandling(EEPROM_I2C, EEPROM_ADDR, 1, I2C_AutoEnd_Mode, I2C_Generate_Start_Read);
  I2C_WAIT_FLAG(EEPROM_I2C, I2C_ISR_RXNE, RESET, ERROR);

  // читаем принятый байт
  *b = I2C_ReceiveData(EEPROM_I2C);
  I2C_WAIT_FLAG(EEPROM_I2C, I2C_ISR_STOPF, RESET, ERROR);

  I2C_ClearFlag(EEPROM_I2C, I2C_ICR_STOPCF);

  return SUCCESS;
}

Пример использования

//какое либо название для того что храним
#define EEPROM_ADDR_SETTING_EXAMPLE 0
...
EEPROMInit();
...
uint8_t setting_example = 1;
if (EEPROMWrite(EEPROM_ADDR_SETTING_EXPAMPLE, setting_example) == SUCCESS) {
  // записали
}

if (EERPOMRead(EEPROM_ADDR_SETTING_EXPAMPLE, &setting_example) == SUCCESS) {
  // прочли
}

На этом все.

При копировании материалов ссылка на https://terraideas.ru/ обязательна

Комментарии к статье: STM32F0x работа с EEPROM 24CXX по I2C

Нет ни одного комментария. Будьте первым!