PROGCONT.RU

Вход Регистрация
О САЙТЕ ГЛАВНАЯ
 

STM32F Flash память сохранение своих данных.

 У многих программистов возникает желание чтобы контроллер запоминал какие то нужные данные даже после выключения, что бы ими воспользоваться при следующем его включении для настройки программы или ещё каких либо манипуляций и для этого STM разработчики реализовали возможность сохранять любые данные в той же части памяти(Main Flash memory) куда прошивается сама программа.
 Как сказал, данные возможно сохранить в тужу часть памяти где находится наш код программы и по этому, что бы исключить перезапись или нарушение кода нужно выбрать то место в памяти где будет это не возможно и для этого мы должны понять как устроена эта Flash память.
 Первым делом вы должны знать какой объём флешь памяти у конкретно вашей модели контроллера, этот параметр указывается цифрой или буквой, зависит от объёма. Например как в моём варианте STM32F103C8T6 цифра 8 которая говорит, что в нём находится 64 Kbytes.

 Для продолжения изучения нам надо определить к какому семейству относится контроллер, для этого смотрим в datasheet stm32f103cb.pdf стр.13, узнаём по объёму памяти что контроллер относится к Medium-density devices.

нет рисунка

 Нам потребуется ещё один документ reference manual en.CD00171190.pdf стр.55 в котором показано как выглядит флешь память данного семейства контроллеров, куда записывается исполняемый код программы.

нет рисунка

 Тут показано что флешь память( Main memory) начинается с 0x0800 0000 адреса и заканчивается 0x0801 FFFF но в нашем случае будет заканчиваться на 0x0800 FFFF, память разбита на страницы Page 0-Page 63, каждая страница имеет размер 1 Kbyte. Первая страница Page 0 начинается с 0x0800 0000 адреса именно с него происходит прошивка загрузчиком флешь памяти программным кодом и заканчивается в зависимости от его длины, вот именно этот промежуток памяти нельзя трогать. Что бы наверняка не ошибиться я предлагаю сохранять свои данные в последнюю страницу Page 63( 0x0800FC00) и вам будет для сохранения данных 1Kbyte.
 Как происходит запись, записи во флешь память является битовой операцией AND шестнадцать бит ваших данных с ячейкой памяти тоже шестнадцать бит и поэтому, что бы записать данные в память там уже должны быть единицы(0xFFFF) а нули выставит загрузчик, проще говоря он может записать только нули ваших данных. Для этого перед сохранением или перезаписью данных, в нужной части памяти должны быть выставлены единицы или произведено стирание, возможно это сделать только с помощью двух специальных функций это стереть всю флешь память или нужную страницу, так задумали разработчики STM, но в тоже время запись данных можно производить по любому чётному адресу и в любом количестве.


 ВНИМАНИЕ! Нельзя:
a) не дождавшись окончания стирания или записи пытаться прочесть из неё какие либо данные;
b) а также перед и вовремя стирания или записи внутренний осциллятор HSI должен быть обязательно включен;
c) производить запись в память только по чётным адресам, в не чётные не допускается.
 Игнорирование этих требований во время манипуляций с памятью приведёт к зависанию контроллера.


Функции чтения данных а также функции SPL для записи данных в память.

 Начнём с функций чтения данных которых нет в SPL, тут три варианта чтение байта(8 бит), полуслово(16 бит) и слово(32 бита), все они принимают тридцати двух битный адрес чтения:
Чтение байта.
   uint8_t Flash_Read_8bit( uint32_t address){ return (*( uint8_t*)address);}
Чтение полуслова.
   uint16_t Flash_Read_16bit( uint32_t address){ return (*( uint16_t*)address);}
Чтение слова.
   uint32_t Flash_Read_32bit( uint32_t address){ return (*( uint32_t*)address);}

Функции SPL:
FLASH_Unlock( )-разблокировать флешь памяти для стирания или записи данных, снимается защита от перезаписи;
FLASH_Lock( )-блокировать флешь памяти, делается после стирания или записи данных, устанавливается защита от перезаписи;
FLASH_Status FLASH_ErasePage( Page_Address)-функция стирает нужную страницу флешь памяти, принимая её адрес, возвращая статус состояния;
FLASH_Status FLASH_EraseAllPages( )-функция стирает всю флешь памяти, возвращая статус состояния;
FLASH_Status FLASH_ProgramWord( Address, Data)-функция записывает в принятый адрес слово(тридцать два бита) данных, возвращая статус состояния;
FLASH_Status FLASH_ProgramHalfWord( Address, Data)-функция записывает в принятый адрес полуслово(шестнадцать бит) данных, возвращая статус состояния;


Практика программирования.

 Простой пример, записываем в память 8 бит, 16 бит и 32 бит данных и читаем их.

#include "stm32f10x.h"
uint8_t Flash_Read_8bit( uint32_t address){ return (*( uint8_t*)address);}
uint16_t Flash_Read_16bit( uint32_t address){ return (*( uint16_t*)address);}
uint32_t Flash_Read_32bit( uint32_t address){ return (*( uint32_t*)address);}

int main(){
//Данные для сохранения.
uint8_t data_save_8_bit= 8;
uint16_t data_save_16_bit= 16;
uint32_t data_save_32_bit= 32;
//Разблокируем память.
FLASH_Unlock();
//Стираем страницу.
FLASH_ErasePage( 0x0800FC00);
//Сохранение данных.
FLASH_ProgramHalfWord( 0x0800FC00, data_save_8_bit);
FLASH_ProgramHalfWord( 0x0800FC00+2, data_save_16_bit);
FLASH_ProgramWord( 0x0800FC00+4, data_save_32_bit);
//Блокировка памяти.
FLASH_Lock();

while(1){
//Чтение данных из памяти.
uint8_t data_8_bit= Flash_Read_8bit( 0x0800FC00);
uint16_t data_16_bit= Flash_Read_16bit( 0x0800FC00+2);
uint32_t data_32_bit= Flash_Read_32bit( 0x0800FC00+4);
}
return 0;
}

 Этот пример если нужно изменить определённые данные в памяти с использование промежуточного массива.

#include "stm32f10x.h"
uint16_t Flash_Read_16bit( uint32_t address){ return (*( uint16_t*)address);}
//Массив для изменения нужных данных.
uint16_t data_arr[5];

int main(){
//Копируем данные из памяти в массив.
for( uint8_t size_arr=0; size_arr<5; size_arr++){
data_arr[ size_arr]= Flash_Read_16bit( 0x0800FC00+size_arr*2);
}
//Изменяем нужный элемент массива.
data_arr[ 3]= 111;

FLASH_Unlock();
FLASH_ErasePage( 0x0800FC00);
//Сохраняем изменённые данные в памяти.
for( uint8_t size_arr=0; size_arr<5; size_arr++){
FLASH_ProgramHalfWord( 0x0800FC00+size_arr*2, data_arr[ size_arr]);
}
FLASH_Lock();

while(1){

}
return 0;
}

 Пример для изменения данных в структуре в которой хранится пять элементов размером шестнадцать бит, использовать восьми битные данные нет смысла потому, что компилятор сохранит их в структуре как шестнадцать бит. С тридцати двух битными данными тоже может возникнуть трудность если они смешаны с элементами другой длины так как они сохраняются в структуре строго через четыре байта а адрес памяти будет кратным четырём.
 Для удобства я вложил код передачи данных через UART что бы просмотреть через программу терминал как изменяется память структуры и Flash, что бы использовать раскомментируйти его или удалите если нет надобности использования. Выводы контроллера используется PB6 UART-TX и PB7 UART-RX для подключения USB-UART конвертера.

#include "stm32f10x.h"
//Адрес Flash памяти где будут хранится данные.
#define adres_start 0x0800FC00
uint16_t Flash_Read_16bit( uint32_t address){ return (*( uint16_t*)address);}
//Структура для данных количество 5 элементов по 16 бит.
typedef struct{
uint16_t data_1;
uint16_t data_2;
uint16_t data_3;
uint16_t data_4;
uint16_t data_5;
} Struc_Data;
//Количество 5 элементов по 16 бит структуры Struc_Data.
uint16_t number_data= 5;

int main(){
/* Настройка UART
RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOB| RCC_APB2Periph_AFIO| RCC_APB2Periph_USART1, ENABLE);
GPIO_InitTypeDef gpio_uart;
gpio_uart.GPIO_Mode=GPIO_Mode_AF_PP;
gpio_uart.GPIO_Pin=GPIO_Pin_6;
gpio_uart.GPIO_Speed=GPIO_Speed_10MHz;
GPIO_Init( GPIOB,&gpio_uart);
gpio_uart.GPIO_Mode=GPIO_Mode_IPU;
gpio_uart.GPIO_Pin=GPIO_Pin_7;
GPIO_Init( GPIOB,&gpio_uart);
GPIO_PinRemapConfig( GPIO_Remap_USART1, ENABLE);
USART_InitTypeDef settings_UART1;
settings_UART1.USART_BaudRate=9600;
settings_UART1.USART_HardwareFlowControl=USART_HardwareFlowControl_None;
settings_UART1.USART_Mode=USART_Mode_Rx|USART_Mode_Tx;
settings_UART1.USART_Parity=USART_Parity_No;
settings_UART1.USART_StopBits=USART_StopBits_1;
settings_UART1.USART_WordLength=USART_WordLength_8b;
USART_Init(USART1,&settings_UART1);
USART_Cmd(USART1, ENABLE);
*/

//Инициализация структуры и назначаем нулевое значение элементам.
Struc_Data struc_copy;
struc_copy.data_1= 0;
struc_copy.data_2= 0;
struc_copy.data_3= 0;
struc_copy.data_4= 0;
struc_copy.data_5= 0;

/* Просматриваем значение элементов через память
for( uint8_t count_data=0; count_data<number_data; count_data++){
uint16_t y=Flash_Read_16bit((uint32_t)&struc_copy+count_data*2); USART_SendData(USART1, y);
while(!USART_GetFlagStatus( USART1, USART_FLAG_TXE));
USART_SendData(USART1, y>>8);
while(!USART_GetFlagStatus( USART1, USART_FLAG_TXE));
for( uint32_t r=10000000; r>0; r--);
}
*/

//Переписываем из Flash памяти значения в структуру.
for( uint8_t count_data=0; count_data<number_data; count_data++){
*(uint16_t *)(( uint32_t)&struc_copy+count_data*2)= Flash_Read_16bit( adres_start+count_data*2);
}

/* Просматриваем значение элементов через память
for( uint8_t count_data=0; count_data<number_data; count_data++){
uint16_t y=Flash_Read_16bit((uint32_t)&struc_copy+count_data*2);
USART_SendData(USART1, y);
while(!USART_GetFlagStatus( USART1, USART_FLAG_TXE));
USART_SendData(USART1, y>>8);
while(!USART_GetFlagStatus( USART1, USART_FLAG_TXE));
for( uint32_t r=10000000; r>0; r--);
}
*/

//Изменяем нужные данные.
struc_copy.data_3= 33;
struc_copy.data_4= 44;
//Записываем изменённые данные в Flash память.
FLASH_Unlock();
FLASH_ErasePage( adres_start);
for( uint8_t count_data=0; count_data<number_data; count_data++){
FLASH_ProgramHalfWord( adres_start+count_data*2, *(uint16_t *)(( uint32_t)&struc_copy+count_data*2));
}
FLASH_Lock();

/* Просматриваем значение элементов через память
for( uint8_t count_data=0; count_data<number_data; count_data++){
uint16_t y=Flash_Read_16bit( adres_start+count_data*2);
USART_SendData(USART1, y);
while(!USART_GetFlagStatus( USART1, USART_FLAG_TXE));
USART_SendData(USART1, y>>8);
while(!USART_GetFlagStatus( USART1, USART_FLAG_TXE));
for( uint32_t r=10000000; r>0; r--);
}
*/

while(1){

}
return 0;
}
Статья №47 Дата:14/11/2019

Комментарии.