Понадобилось писать данные в энергонезависимую память. Краткий отчет.

Итак появилась такая задача — при вытаскивании батарейки из девайса, нужно сохранить пару переменных до того как питалово окончательно отрубится. В качестве схемы было использовано такое решение.
sch_bat

Когда батарейка будет вытащена, АЦП это зафиксирует, при этом микроконтроллер еще какое то время будет жить на запасе кондера C1. После того, как батарейку поменяют, нужно продолжить с того места, где остановились в прошлый раз. В общем нужно, где то сохранить пару переменных, в энергонезависимой памяти.

Было несколько вариантов: EEPROM, Flash, backup регистры. Внешние девайсы для хранения не рассматривались. Еепром, отмелась, хоть в используемом мной камне STM32L1 она есть, но случаи когда нужно переносить код с одной серии на другую уже проходили. А как известно еепром есть не во всех сериях стм. Поэтому вариант отмелся сам собой. Backup регистры, вариант интересный, даже очень, но для него нужна внешняя батарейка. Собственно, кроме флеша ничего не осталось.

Из инфы про флеш. Писать можно байтами, а вот стирать можно только страницами, ресурс около 10000 раз. Процесс стирания требует времени. Выбирать место для записи лучше ближе к концу флеша, чтобы не затереть данные программы. Для примера я взял адрес 0x08030000.
stm_flash

Для того, чтобы записать данные, нужно сначала разлочить память, стереть страницу, записать, залочить обратно flash.
С SPL это делается это просто, очень просто.

FLASH_Unlock();
FLASH_ErasePage(0x8030000); //стираем страницу
FLASH_FastProgramWord(0x8030000, 0xEFDC9988); //пишем 4 байта по адресу 0x8030000
 
FLASH_Lock();

Собственно все, данные на месте, читаются норм после выключения, можно посмотреть отладчиком memory, но пожалуй самый большой вопрос, который меня интересовал это время стирания/записи, хотя бы примерное. Для этого было решено задействовать таймер. Запускаем таймер, обнуляем счетный регистр, выполняем операции с flash, останавливаем таймер, смотрим сколько натикало.

Чтобы проверить, что данный способ покажет то, что ожидается, предварительно проведено испытание на обычном цикле мигания светодиодом.

while(1)
{	
 //длительность данной операции нужно посчитать
  for(uint16_t i = 0; i < 65000; i++);  
  GPIO_ToggleBits(GPIOB, GPIO_Pin_7);
}

Осцилл показал, что частота ногодрыга 15.3Гц,
freq_operation

т.е один такой цикл выполняется за 1/(15.3*2) = 0.032сек. Нужно было с помощью таймера получить нечто похожее.

Заводим таймер, в моем случае это TIM7. Настраиваем прерывание по переполнению. Значение предделителя и периода было взято от балды.

// FREQ = F_CPU/(PRESC) * PERIOD
TIM_TimeBaseInitTypeDef       tim_setup7;
tim_setup7.TIM_ClockDivision   = TIM_CKD_DIV1; //без предделителя частоты
tim_setup7.TIM_CounterMode     = TIM_CounterMode_Up; //считаем вверх
tim_setup7.TIM_Period          = 2000-1; //считать до 2000, затем обнулить
tim_setup7.TIM_Prescaler       = 16000; //предделитель 16000
 
TIM_TimeBaseInit(TIM7, &tim_setup7);
//TIM_Cmd(TIM7, ENABLE);
 
TIM_ITConfig(TIM7, TIM_IT_Update, ENABLE); //включаем прерывание по переполнению
NVIC_EnableIRQ(TIM7_IRQn); //разрешаем прерывания от таймера

В основном цикле, там где нужно считать длительность — до операции включаем таймер, после операции выключаем. Значение счетного регистра и будет результатом. Останется только пересчитать его из тиков в секунды.

while(1)
{
  TIM_Cmd(TIM7, ENABLE);    //включаем таймер
  TIM7->CNT = 0;            //обнуляем счетный регистр
  COUNTS = 0;               //обнуляем переменную
 
  for(uint16_t i = 0; i < 65000; i++);  //длительность данной операции нужно посчитать
  GPIO_ToggleBits(GPIOB, GPIO_Pin_7);
 
  TIM_Cmd(TIM7, DISABLE);   //выключаем таймер, чтобы не тикал
  COUNTS = TIM7->CNT;       //забираем количество тиков 
}

Запускаем отладчик, ставим 3 бряки: 1. до выполнения кода; 2. после выполнения; 3. внутри прерывания от таймера. Запускаем прогу, стартуем со первой бряки, тормозим на второй, смотрим сколько натикало. В моем случае вышло 0x20 или 32. Таймер работает на частоте F_CPU/предделитель = 16 000 000/16 000 = 1 кГц, т.е. 1 тик 0.001сек. Итого длительность тупняка с переключением ножки заняла 0.001*32 = 0.0032 сек по таймерным подсчетам. Это полностью совпадает с показаниями осцилла.

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

Таким же образом, был произведен замер стирания и записи 4 байт во флеш. С тем же таймером и предделителем у меня натикало 5мс.

4 комментария: STM32 работа с Flash.

  • Можно наверное не на АЦП, а на порт, имеющий прерывание по спаду\изменению уровня, должно получиться быстрее.
    зы: Интересно, насколько безопасно писать в память программ при пропадании питания? Не знаю как сейчас, а лет 10 назад на microchip.ru было больше десятка тем, вида «при выключении девайса слетела прошивка», и там народ объяснял подобный эффект тем, что якобы при снижении или медленном нарастании питания процессор может начать выполнять код не в том порядке, как он заложен в программе, а в случайном, и в и.ч. выполнить процедуру стирания FLASH, причем сектор с кодом, а не предназначенный для этого.

  • ну тут питание формально не пропадает, для мк оно еще какое то время держится за счет кондера. по поводу надежности мне еще предстоит это исследование, как доберусь, так дополню.

  • При помощи вашего рецепта у меня получилось с переменки записать данные нужную ячейку. А вот как их от туда вытащить?

  • указываете какой нужно адрес, размерность и читаете по указателю.
    uint32_t x = (*(volatile uint32_t*)address);

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

Можно использовать следующие HTML-теги и атрибуты: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

Последние комментарии
  • Загрузка...
Архивы
Счетчик
Яндекс.Метрика