16. STM32. Програмування STM32F103. Flash


27.09.2016

Робота із Flash пам`яттю

STM32 не має енергонезалежної EEPROM. EEPROM мають тільки STM32L. Так сталося, що у STMicroelectronics вирішили, ніби EEPROM потрібна тільки для Ultra Low Power серії мікроконтролерів. А що ж нам робити? Нам потрібна енергонезалежна пам`ять. Де нам зберігати наші дані, налаштування, тощо? У Flash! Так, у тій самій пам`яті, де лежить програма мікроконтролера. STM32 може писати у Flash пам`ять.

Для того, щоб безпечно щось записати у Flash пам`ять, треба спочатку з`ясувати її структуру. Нас цікавить Main memory. Саме в ній зберігається програма і в цю пам`ять ми будемо писати. Звісно, у не зайняту програмою ділянку пам`яті. Flash пам`ять розбита на сторінки. Кількість і розмір сторінок у різних контролерах різна. Про структуру пам`яті Вашого мікроконтролера читайте у Programming manual.

stm32_flash

Ця таблиця наведена для усіх STM32F103 medium-density devices. Наш контролер (STM32F103C8) має 64 Кб Flash пам`яті. Тобто, адреса початку останньої сторінки 0x800FC00.

А тепер "приколи", тобто обмеження при роботі з Flash:

  • Писати можна тільки у попередньо витерту пам`ять. У стертої пам`яті усі біти встановлені в "1".
  • Витирати можна тільки сторінку цілком. Тобто, ми не можемо витерти 1, 2, чи 10 байтів, тільки цілком сторінку. У нашому випадку - це блок у 1 КБ.
  • Писати у Flash треба словами по 32 біта.
Якщо нам треба зберігати у Flash якісь налаштування, краще за все це робити на останніх сторінках Flash пам`яті. Ми будемо використовувати останню сторінку. Тому я раніше і згадував про адресу останньої сторінки. Ще нам треба слідкувати за розміром програми. Якщо розмір прошивки буде залазити на останню сторінку - це проблема. Ми не зможемо гарантувати цілісність даних і самої прошивки.

Тепер перейдемо до практичної реалізації.

Перед тим, яки починати роботу з Flash треба виконати пару функцій - FLASH_PrefetchBufferCm, FLASH_SetLatency.


void FLASH_Init(void) {
	/* Next commands may be used in SysClock initialization function
	   In this case using of FLASH_Init is not obligatorily */
	/* Enable Prefetch Buffer */
	FLASH_PrefetchBufferCmd( FLASH_PrefetchBuffer_Enable);
	/* Flash 2 wait state */
	FLASH_SetLatency( FLASH_Latency_2);
}

Ці функції можуть бути виконані під час налаштування робочої частоти. Дивись статтю "4. STM32. Програмування STM32F103. Тактування"

FLASH Latency рекомендовано встановлювати: FLASH_Latency_0 - 0 < SYSCLK≤ 24 MHz FLASH_Latency_1 - 24 MHz < SYSCLK ≤ 48 MHz FLASH_Latency_2 - 48 MHz < SYSCLK ≤ 72 MHz

Для запису у Flash пам`ять треба її розлочить, стерти сторінку, записати інформацію, залочити Flash:


FLASH_Unlock();
FLASH_ErasePage(MY_FLASH_PAGE_ADDR);
FLASH_ProgramWord((uint32_t)address1, (uint32_t)data1);
FLASH_ProgramWord((uint32_t)address2, (uint32_t)data2);
...
FLASH_ProgramWord((uint32_t)addressn, (uint32_t)datan);
FLASH_Lock();

Читати з флеш:


(*(__IO uint32_t*)address);

Читати 32-бітними словами не завжди зручно і, зазвичай, нам треба зберігати декілька налаштувань різного типу. Для спрощення цього процесу зробимо структуру з параметрами, які нам треба зберігати:


typedef	struct
  {
	char Parameter1;		// 1 byte
	uint8_t Parameter2;		// 1 byte
	uint16_t Parameter3;	// 2 byte
	uint32_t Parameter4;	// 4 byte
							// 8 byte = 2 * 32 bits words.  It`s - OK
							// !!! Full size (bytes) must be a multiple of 4 !!!
  } tpSettings;

Одна вимога до цієї структури: її довжина має бути кратна 32 бітам, тобто кратна 4 байтам, оскільки ми можемо писати у Flash пам`ять тільки 32 бітними словами. Наприклад, створимо таку структуру, з трьома параметрами:


typedef	struct
  {
	char Parameter1;		// 1 byte
	uint8_t Parameter2;		// 1 byte
	unsigned char Parameter3;	// 1 byte
							// 3 byte = 24 bits.  It`s NOT OK!
							// !!! Full size (bytes) must be a multiple of 4 !!!
  } tpSettings;

Ці параметри займають тільки 3 байти, тобто 24 біти. Треба "дотягнути" розмір до цілого 32-бітного слова. Можна просто добавити додатковий параметр, назвемо його nothing:


typedef	struct
  {
	char Parameter1;		// 1 byte
	uint8_t Parameter2;		// 1 byte
	unsigned char Parameter3;	// 1 byte

	uint8_t nothing;		// 1 byte

							// 4 byte = 32 bits.  It`s - OK
							// !!! Full size (bytes) must be a multiple of 4 !!!
  } tpSettings;

Тепер нам знадобиться дві функції для зчитування і запису нашої структури:


void FLASH_ReadSettings(void) {
	//Read settings
	uint32_t *source_addr = (uint32_t *)MY_FLASH_PAGE_ADDR;
	uint32_t *dest_addr = (void *)&settings;
	for (uint16_t i=0; i<SETTINGS_WORDS; i++) {
		*dest_addr = *(__IO uint32_t*)source_addr;
		source_addr++;
		dest_addr++;
	}
}

void FLASH_WriteSettings(void) {
	FLASH_Unlock();
	FLASH_ErasePage(MY_FLASH_PAGE_ADDR);

	// Write settings
	uint32_t *source_addr = (void *)&settings;
	uint32_t *dest_addr = (uint32_t *) MY_FLASH_PAGE_ADDR;
	for (uint16_t i=0; i<SETTINGS_WORDS; i++) {
		FLASH_ProgramWord((uint32_t)dest_addr, *source_addr);
		source_addr++;
		dest_addr++;
	}

	FLASH_Lock();
}

MY_FLASH_PAGE_ADDR - це адреса сторінки, де ми будемо зберігати наші налаштування. SETTINGS_WORDS - кількість 32-бітних слів, які займає структура.

Повний код програми:


#include "stm32f10x.h"
#include "stm32f10x_rcc.h"
#include "stm32f10x_flash.h"


// ====================================================
// FLASH Settings struct
// ====================================================
#define MY_FLASH_PAGE_ADDR 0x800FC00

typedef	struct
  {
	char Parameter1;		// 1 byte
	uint8_t Parameter2;		// 1 byte
	uint16_t Parameter3;	// 2 byte
	uint32_t Parameter4;	// 4 byte

							// 8 byte = 2  32-bits words.  It`s - OK
							// !!! Full size (bytes) must be a multiple of 4 !!!
  } tpSettings;

tpSettings settings;

#define SETTINGS_WORDS sizeof(settings)/4

void FLASH_Init(void) {
	/* Next commands may be used in SysClock initialization function
	   In this case using of FLASH_Init is not obligatorily */
	/* Enable Prefetch Buffer */
	FLASH_PrefetchBufferCmd( FLASH_PrefetchBuffer_Enable);
	/* Flash 2 wait state */
	FLASH_SetLatency( FLASH_Latency_2);
}

void FLASH_ReadSettings(void) {
	//Read settings
	uint32_t *source_addr = (uint32_t *)MY_FLASH_PAGE_ADDR;
	uint32_t *dest_addr = (void *)&settings;
	for (uint16_t i=0; i<SETTINGS_WORDS; i++) {
		*dest_addr = *(__IO uint32_t*)source_addr;
		source_addr++;
		dest_addr++;
	}
}

void FLASH_WriteSettings(void) {
	FLASH_Unlock();
	FLASH_ErasePage(MY_FLASH_PAGE_ADDR);

	// Write settings
	uint32_t *source_addr = (void *)&settings;
	uint32_t *dest_addr = (uint32_t *) MY_FLASH_PAGE_ADDR;
	for (uint16_t i=0; i<SETTINGS_WORDS; i++) {
		FLASH_ProgramWord((uint32_t)dest_addr, *source_addr);
		source_addr++;
		dest_addr++;
	}

	FLASH_Lock();
}
// ====================================================


int main(void)
{
    FLASH_Init();
    FLASH_ReadSettings();

    settings.Parameter1++;
    settings.Parameter2++;
    settings.Parameter3++;
    settings.Parameter4++;

    FLASH_WriteSettings();

    while (1)
    {

    }
}

Пам`ятка

Треба пам`ятати, що Flash пам`ять має ліміт кількості циклів запису. Згідно до документації - мінімум 10000. Це не дуже і багато, якщо досить інтенсивно використовувати Flash пам`ять. Але для зберігання налаштувань цілком достатньо.

UPD: Важливо пам'ятати, що для запису у Flash використовуэться HSI - внутрішній тактовий генератор. HSI має бути увімкнений під час запису у Flash.

Бажаю успіхів!

Дивись також:

STM32
Коментарі:
barteroff говорить:
27.09.2016 09:03
Ресурс запису можна також збілшити якщо використати принаймні 2 сторінки флеша, заповнюючи кожну по черзі.
Цікавий апноут на цю тему від ST з реалізацією для F4:
http://www.st.com/content/ccc/resource/technical/document/application_note/ec/dd/8e/a8/39/49/4f/e5/DM00036065.pdf/files/DM00036065.pdf/jcr:content/translations/en.DM00036065.pdf

mikhail говорить:
16.03.2020 19:44
А точно адреса початку останньої сторінки 0x800FC00 ? Не 0x801FC00 ?

Додати коментар
Code
* - обов'язкові поля

Архіви