173а. Пропадание питания: сколько миллисекунд есть, PVD и BOR, что успеть записать
Подраздел 10.7 говорит коротко: детектор напряжения предупредит, а программа успеет сохранить данные. Здесь — сколько именно она успеет, где предупреждение получить раньше, что в эти миллисекунды вообще можно записать и как убедиться, что сохранённое переживает тысячу выдернутых вилок. Раздел 173 защищал вход платы от переполюсовки и выбросов; этот — от обычного отключения.
Все времена ниже — модель ngspice и таблицы даташитов F103 и F407; обработчик PVD и настройка BOR собраны armclang, журнал с обрывами прогнан на ПК.
Как это называется в документации
Оригинал Перевод programmable voltage detector (PVD) программируемый детектор напряжения power-on / power-down reset (POR/PDR) сброс при включении / при выключении brownout reset (BOR) сброс при просадке питания hold-up time время удержания: сколько живёт схема после пропадания входа bulk capacitor накопительный конденсатор supercapacitor ионистор backup domain / backup registers резервный домен / резервные регистры backup SRAM резервное ОЗУ option bytes байты конфигурации torn write оборванная запись
173а.1 Сколько времени есть
Пока вход был, конденсаторы шины заряжены до рабочего напряжения. Вход пропал — нагрузка продолжает брать свой ток, и напряжение падает. Для линейного стабилизатора и самого камня нагрузка — почти постоянный ток, и время считается в одну строку:
t = C · ΔU / I,
где ΔU — от порога, на котором программа узнала о беде, до нижней границы
питания камня. У F103 нижняя граница — 2,0 В (таблица 9 даташита). Порог PVD
с именем PWR_CR_PLS_2V9 на спаде — 2,78 В типично и 2,66 В в худшем случае
(таблица 11). Значит, ΔU = 0,78 В, а считать надёжно — по 0,66 В.
Шина 3,3 В, нагрузка 25 мА:
| Ёмкость на шине | Типично (0,78 В) | Худший порог (0,66 В) |
|---|---|---|
| 10 мкФ | 0,312 мс | 0,264 мс |
| 100 мкФ | 3,12 мс | 2,64 мс |
| 470 мкФ | 14,7 мс | 12,4 мс |
| 1000 мкФ | 31,2 мс | 26,4 мс |
Типичная учебная плата держит на шине 3,3 В десяток микрофарад — это четверть миллисекунды. Запись одной восьмибайтной записи во Flash F103 в худшем случае длится 0,28 мс (173а.4), то есть на голой плате она может и не уложиться.
Для импульсного стабилизатора формула другая: он берёт со входа постоянную мощность, а не ток, и конденсатор отдаёт энергию:
t = C · (U₁² − U₂²) / (2P).
Те же 470 мкФ на входе 12 В, от 9,0 до 4,5 В, при P = 3,3 В · 25 мА / 0,85 = 0,097 Вт дают 147 мс; линейный стабилизатор с тех же 9,0 В до 4,3 В дотянул бы 73,6 мс. Импульсный лучше использует высокое напряжение: чем ниже вход, тем больше тока он просит, но и энергии в конденсаторе ещё много.
173а.2 Где ловить: PVD внутри или компаратор на входе
PVD смотрит на VDD самого камня — то есть уже на выход стабилизатора. Пока стабилизатор держит 3,3 В, PVD молчит, хотя вход давно падает. Предупреждение приходит, когда запаса почти не осталось.
Модель: 12 В через диод Шоттки заряжают 470 мкФ на входе, дальше линейный стабилизатор (падение 1 В, собственный ток 5 мА), 10 мкФ на выходе, нагрузка 25 мА. Источник пропадает на 10-й миллисекунде:
| Где ловить | 470 мкФ на входе | 10 мкФ на входе |
|---|---|---|
| компаратор на входе, порог 9 В | 94,4 мс | 2,43 мс |
| PVD, 2,78 В (типично) | 12,5 мс | 0,52 мс |
| PVD, 2,66 В (худший порог) | 10,6 мс | 0,44 мс |
Разница — в 7,5 раза при большом конденсаторе и в 4,7 раза при маленьком. Выигрыш даёт не хитрость компаратора, а напряжение: от 9 до 4,3 В конденсатор отдаёт 4,7 В заряда, а в окне PVD — 0,78 В.
Порог на входе ставят делителем. 100 кОм и 22 кОм превращают 9,0 В в 1,623 В; для АЦП F103 с опорой 3,3 В это код 2014. Аналоговый сторож АЦП (раздел 14) вызовет прерывание, как только канал опустится ниже этого кода, а опора в этот момент ещё честные 3,3 В — стабилизатор пока держит. Обычный вход GPIO с делителем для этого не годится: его порог гуляет между 0,35 и 0,65 VDD.
Внутренний PVD всё равно стоит включить — как второй рубеж. Он ловит то, чего не видит вход: просадку от собственного перегруза, отключение уже после стабилизатора, питание от USB в обход входа.
173а.3 BOR: сброс, который не даёт работать на краю
Детектор только предупреждает. Если питание продолжает падать, камень должен вовремя остановиться, а не работать на напряжении, при котором Flash читается с ошибками.
F103 умеет только POR/PDR: сброс на спаде при 1,88 В типично (1,8–1,96 В),
на подъёме — при 1,92 В (1,84–2,0 В). Нижняя граница питания при этом — 2,0 В.
Между порогом сброса и 2,0 В лежит полоса, где камень уже не обязан работать
правильно, но ещё не сброшен: ядро может выполнить мусор вместо команды. Если
в этот момент Flash отперта, случайная запись попадёт куда угодно. Отсюда два
правила: Flash держать запертой (FLASH_CR_LOCK) всё время, кроме самой
записи, а там, где сбой дорог, ставить внешний супервизор питания — микросхему
сброса с порогом, скажем, 2,9 В. Та же полоса мешает при медленном нарастании
питания от ионистора или солнечной панели: камень стартует при 1,92 В, падает
от первого броска тока, снова стартует — дребезг сбросов из 61.3.
F407 имеет настоящий BOR с тремя уровнями в байтах конфигурации:
Биты BOR_LEV |
Уровень | Спад, В (мин./тип./макс.) | Подъём, тип., В |
|---|---|---|---|
| 11 | выключен: действует POR/PDR | 1,60 / 1,68 / 1,76 | 1,72 |
| 10 | 1 | 2,13 / 2,19 / 2,24 | 2,29 |
| 01 | 2 | 2,44 / 2,50 / 2,56 | 2,59 |
| 00 | 3 | 2,75 / 2,83 / 2,88 | 2,92 |
С завода BOR выключен (биты 11). А запись во Flash F407 словами по 32 бита
(PSIZE = x32, так пишет HAL при диапазоне 2,7–3,6 В) разрешена только от
2,7 В; полусловами — от 2,1 В, байтами — от 1,8 В. Если прошивка пишет во Flash
словами, BOR ставят на уровень 3 — тогда камень сбросится раньше, чем питание
уйдёт ниже 2,7 В:
void bor_level3(void)
{
if ((FLASH->OPTCR & FLASH_OPTCR_BOR_LEV) == 0) return; /* уже 00 — уровень 3 */
FLASH->OPTKEYR = 0x08192A3Bu;
FLASH->OPTKEYR = 0x4C5D6E7Fu;
FLASH->OPTCR &= ~FLASH_OPTCR_BOR_LEV; /* 00 — уровень 3: спад 2,75…2,88 В */
FLASH->OPTCR |= FLASH_OPTCR_OPTSTRT;
while (FLASH->SR & FLASH_SR_BSY) { }
FLASH->OPTCR |= FLASH_OPTCR_OPTLOCK;
}
Байты конфигурации переписываются один раз — проверка в первой строке не даёт стирать их при каждом включении.
173а.4 Что успеть записать
Бюджет — единицы или десятки миллисекунд. Таблицы Flash из даташитов:
| Операция | F103 | F407 |
|---|---|---|
| стереть | страница 1 КБ: 20–40 мс | сектор 16 КБ (x32): 250–500 мс, 128 КБ: 1–2 с |
| записать 8 байт | 4 полуслова × 40–70 мкс = 0,16–0,28 мс | 2 слова (x32) × 16–100 мкс = 0,032–0,2 мс |
| ток записи и стирания | до 5 мА сверх обычного | 12 мА (x32, 3,3 В) |
| резервные регистры | 10 × 16 бит = 20 байт | 20 × 32 бита = 80 байт |
| резервное ОЗУ | нет | 4 КБ |
Вывод из таблицы один: стирать в момент пропадания нельзя. Страница F103 стирается дольше, чем живёт даже плата с 470 мкФ, а сектор F407 — в десятки раз дольше. Писать можно только в заранее стёртое место — поэтому в 11.8 и 42.6 запись идёт журналом: новая запись дописывается в чистый слот, старая остаётся. Стирают страницу потом, при нормальном питании, когда журнал заполнился.
Обработчик PVD для F103 дописывает один слот из четырёх полуслов — данные, контроль и метку, метку последней:
void pvd_init(void)
{
RCC->APB1ENR |= RCC_APB1ENR_PWREN;
PWR->CR = (PWR->CR & ~PWR_CR_PLS) | PWR_CR_PLS_2V9 | PWR_CR_PVDE; /* спад: 2,66…2,90 В */
EXTI->IMR |= EXTI_IMR_MR16;
EXTI->RTSR |= EXTI_RTSR_TR16; /* PVDO = 1, когда питание НИЖЕ порога */
NVIC_SetPriority(PVD_IRQn, 0);
NVIC_EnableIRQ(PVD_IRQn);
}
void PVD_IRQHandler(void)
{
EXTI->PR = EXTI_PR_PR16;
GPIOB->BSRR = GPIO_BSRR_BR12; /* снять нагрузку: подсветку, радио, реле */
uint32_t v = counter;
if (next_slot + 8u <= LOG_END) { /* страница кончилась — стирать некогда */
FLASH->KEYR = 0x45670123u;
FLASH->KEYR = 0xCDEF89ABu;
flash_write16(next_slot + 2u, (uint16_t)v);
flash_write16(next_slot + 4u, (uint16_t)(v >> 16));
flash_write16(next_slot + 6u, (uint16_t)~(v ^ (v >> 16))); /* контроль */
flash_write16(next_slot, REC_MAGIC); /* метка — последней */
FLASH->CR |= FLASH_CR_LOCK;
next_slot += 8u;
}
while (PWR->CSR & PWR_CSR_PVDO) { } /* был провал, а не отключение: ждём возврата */
NVIC_SystemReset();
}
Три детали. Прерывание EXTI16 настроено на нарастающий фронт: флаг PVDO
становится единицей, когда питание опускается ниже порога. Приоритет — высший:
обработчик не должен ждать, пока закончит работу кто-то другой. И обработчик
не зависает навсегда: если питание вернулось (был провал, а не отключение),
камень перезапускается и продолжает работу.
Резервные регистры и резервное ОЗУ быстрее Flash на три порядка: запись — это такты, а не микросекунды. Но они живут, только пока питание есть на выводе VBAT. У F103 без батарейки на VBAT резервный домен питается от VDD и теряет всё вместе с ним. У F407 4 КБ резервного ОЗУ держит отдельный маломощный стабилизатор, его включают явно:
void bkpsram_on(void)
{
RCC->APB1ENR |= RCC_APB1ENR_PWREN;
PWR->CR |= PWR_CR_DBP; /* снять защиту резервного домена */
RCC->AHB1ENR |= RCC_AHB1ENR_BKPSRAMEN;
PWR->CSR |= PWR_CSR_BRE; /* резервный стабилизатор: 4 КБ живут на VBAT */
while (!(PWR->CSR & PWR_CSR_BRR)) { }
}
С батарейкой на VBAT «последние слова» — причину, счётчик, время — пишут в резервное ОЗУ даже при бюджете в полмиллисекунды, а во Flash их переносят после следующего включения, когда времени сколько угодно.
173а.5 Запас энергии и разгрузка
Если бюджета не хватает, есть два рычага: добавить энергии или уменьшить ток.
Конденсатор — на высоком напряжении и за диодом. Для линейного стабилизатора считается заряд C · ΔU, и тот же конденсатор на входе 12 В даёт в 7,5 раза больше времени, чем на шине 3,3 В (173а.2). Диод перед ним (раздел 169, 169.6) обязателен: без диода конденсатор отдаст заряд назад в провод и во все остальные нагрузки на той же линии. Большой конденсатор на входе при включении берёт бросок тока — его ограничивают, как в 172.5.
Ионистор — когда нужны секунды. 0,1 Ф на шине 3,3 В за диодом Шоттки (падение около 0,3 В) держит камень от срабатывания PVD до 2,0 В около 3 с при 25 мА. Заряжают его через резистор: 33 Ом при пустом ионисторе дают бросок 100 мА, а заряжается он пять постоянных времени — 5 · 33 Ом · 0,1 Ф = 16,5 с. Диод параллельно резистору пропускает ток разряда в обход. До полной зарядки прибор не должен рассчитывать на запас: напряжение ионистора меряют АЦП и разрешают запись только после порога.
Снять нагрузку первым делом. Подсветка, радиомодуль, реле, двигатель берут больше, чем камень. Первая строка обработчика PVD выключает их — и время удержания растёт во столько раз, во сколько упал ток. Туда же — перейти на низкую частоту: ядро на 8 МГц от HSI берёт в разы меньше, чем на 72 МГц. Но переключение частоты тоже время, и для Flash нужно поправить число тактов ожидания (раздел 6), так что сначала — выключить внешнее, потом писать.
173а.6 Проверка: тысяча обрывов
Оборванную запись нельзя проверить глазами: чаще всего всё работает, а раз в сто выключений данные превращаются в мусор. Проверяют числом.
На ПК. Модель страницы Flash F103: стёртое полуслово — 0xFFFF, запись
только сбрасывает биты, стирание их поднимает; оборванная операция оставляет
каждый бит, который должен был измениться, наполовину — изменённым или нет.
Тысяча обрывов в случайный момент сохранения, два способа:
| Способ | Длится | Новое | Старое | Мусор |
|---|---|---|---|---|
| стереть страницу и записать значение | 40,14 мс | 0 | 0 | 1000 |
| дописать в журнал: данные, контроль, метка | 0,28 мс | 3 | 997 | 0 |
Наивный способ портит данные при каждом обрыве: стирание длинное, и обрыв почти всегда приходится на него. Журнал ни разу не отдал мусор: читатель находит либо новую запись, либо предыдущую. Три «новых» — случаи, когда метка успела лечь целиком ровно в момент обрыва, а данные до неё уже были записаны. Обрыв здесь — посреди самой записи, худший случай; если запись начинается по сигналу PVD и укладывается в бюджет, журнал сохраняет новое значение всякий раз.
На стенде. Модель не знает всего про реальную Flash, поэтому изделие проверяют выключениями: вторая плата управляет ключом в питании испытуемой (раздел 171), отключает его через случайное время после каждой записи и снова включает через секунду. Испытуемая после старта проверяет журнал и отправляет в UART номер последней целой записи; вторая плата считает, сколько раз номер вернулся назад больше чем на одну запись и сколько раз журнал оказался испорчен. Тысяча циклов по две секунды — чуть больше получаса. Во время испытания отладчик отключают: ST-Link питает плату через свой вывод 3,3 В, и выключение становится ненастоящим.
173а.7 Грабли
- Стереть страницу в обработчике PVD. Стирание страницы F103 — до 40 мс, сектора F407 — до 500 мс для 16 КБ. Бюджета не хватит никогда; страница останется наполовину стёртой, и пропадут и старые, и новые данные.
- Имя порога принято за порог.
PWR_CR_PLS_2V9на спаде срабатывает при 2,66–2,90 В, типично 2,78 В. Бюджет считают по худшему — по 2,66 В. - Спадающий фронт на EXTI16. Флаг
PVDO— единица, когда питание ниже порога. НастроитьFTSR— значит получить прерывание при возврате питания, когда сохранять уже нечего. - Обработчик висит в пустом цикле. Провал на 20 мс не выключает камень:
питание вернулось, а прошивка стоит в
for (;;)до ручного сброса. ЖдатьPVDO = 0и перезапускаться. - Конденсатор без диода. Заряд уходит в соседние нагрузки и в проводку; расчётные миллисекунды превращаются в доли миллисекунды.
- BOR F407 оставлен выключенным. Камень работает до 1,7 В, а запись словами по 32 бита разрешена только от 2,7 В. Уровень 3 в байтах конфигурации.
- Резервные регистры без батарейки. Без VBAT резервный домен F103 питается от VDD и забывает всё вместе с ним.
- Проверка с подключённым отладчиком. ST-Link держит плату по выводу 3,3 В; выключение источника ничего не выключает.
173а.8 Итоги
- Время удержания для линейного стабилизатора — t = C · ΔU / I, для импульсного — t = C · (U₁² − U₂²) / (2P). На шине 3,3 В при 25 мА 10 мкФ дают 0,264 мс, 470 мкФ — 12,4 мс по худшему порогу PVD.
PWR_CR_PLS_2V9у F103 на спаде — 2,66–2,90 В; нижняя граница питания — 2,0 В. Компаратор на входе 12 В с 470 мкФ даёт 94,4 мс, PVD — 12,5 мс.- F103 знает только POR/PDR (спад 1,88 В), F407 — BOR трёх уровней, с завода выключенный; запись во Flash F407 по 32 бита — только от 2,7 В.
- В момент пропадания Flash только дописывают в заранее стёртое место: стирание — 20–40 мс у F103 и сотни миллисекунд у F407, запись 8 байт — до 0,28 мс.
- Резервные регистры и резервное ОЗУ — быстрые, но только с батарейкой на VBAT.
- Тысяча обрывов посреди сохранения: «стереть и записать» — мусор 1000 раз, журнал с меткой последней — ни разу.
Полные исходники
pvd173a_f1.c — F103: PVD на EXTI16 и «последние слова» в заранее стёртую страницу.
/* 173а: F103 — PVD на EXTI16 и «последние слова» в заранее стёртую страницу Flash.
Собирается armclang против stm32f10x.h пака Keil (проверка _verify/pwr173a_check.py). */
#include <stdint.h>
#define LOG_PAGE 0x0800FC00u /* последняя страница 64 КБ, стёрта заранее */
#define LOG_END (LOG_PAGE + 1024u)
#define REC_MAGIC 0xA55Au
volatile uint32_t counter; /* то, что жалко потерять */
static uint32_t next_slot = LOG_PAGE; /* первый чистый слот, ищется при старте */
void pvd_init(void)
{
RCC->APB1ENR |= RCC_APB1ENR_PWREN;
PWR->CR = (PWR->CR & ~PWR_CR_PLS) | PWR_CR_PLS_2V9 | PWR_CR_PVDE; /* спад: 2,66…2,90 В */
EXTI->IMR |= EXTI_IMR_MR16;
EXTI->RTSR |= EXTI_RTSR_TR16; /* PVDO = 1, когда питание НИЖЕ порога */
NVIC_SetPriority(PVD_IRQn, 0);
NVIC_EnableIRQ(PVD_IRQn);
}
static void flash_write16(uint32_t addr, uint16_t v)
{
FLASH->CR |= FLASH_CR_PG;
*(volatile uint16_t *)addr = v;
while (FLASH->SR & FLASH_SR_BSY) { }
FLASH->CR &= ~FLASH_CR_PG;
}
void PVD_IRQHandler(void)
{
EXTI->PR = EXTI_PR_PR16;
GPIOB->BSRR = GPIO_BSRR_BR12; /* снять нагрузку: подсветку, радио, реле */
uint32_t v = counter;
if (next_slot + 8u <= LOG_END) { /* страница кончилась — стирать некогда */
FLASH->KEYR = 0x45670123u;
FLASH->KEYR = 0xCDEF89ABu;
flash_write16(next_slot + 2u, (uint16_t)v);
flash_write16(next_slot + 4u, (uint16_t)(v >> 16));
flash_write16(next_slot + 6u, (uint16_t)~(v ^ (v >> 16))); /* контроль */
flash_write16(next_slot, REC_MAGIC); /* метка — последней */
FLASH->CR |= FLASH_CR_LOCK;
next_slot += 8u;
}
while (PWR->CSR & PWR_CSR_PVDO) { } /* был провал, а не отключение: ждём возврата */
NVIC_SystemReset();
}
bor173a_f4.c — F407: уровень BOR в байтах конфигурации и резервное ОЗУ на батарейке.
/* 173а: F407 — уровень BOR в байтах конфигурации и резервное ОЗУ на батарейке.
Собирается armclang против stm32f4xx.h пака Keil (проверка _verify/pwr173a_check.py). */
#include <stdint.h>
void bor_level3(void)
{
if ((FLASH->OPTCR & FLASH_OPTCR_BOR_LEV) == 0) return; /* уже 00 — уровень 3 */
FLASH->OPTKEYR = 0x08192A3Bu;
FLASH->OPTKEYR = 0x4C5D6E7Fu;
FLASH->OPTCR &= ~FLASH_OPTCR_BOR_LEV; /* 00 — уровень 3: спад 2,75…2,88 В */
FLASH->OPTCR |= FLASH_OPTCR_OPTSTRT;
while (FLASH->SR & FLASH_SR_BSY) { }
FLASH->OPTCR |= FLASH_OPTCR_OPTLOCK;
}
void bkpsram_on(void)
{
RCC->APB1ENR |= RCC_APB1ENR_PWREN;
PWR->CR |= PWR_CR_DBP; /* снять защиту резервного домена */
RCC->AHB1ENR |= RCC_AHB1ENR_BKPSRAMEN;
PWR->CSR |= PWR_CSR_BRE; /* резервный стабилизатор: 4 КБ живут на VBAT */
while (!(PWR->CSR & PWR_CSR_BRR)) { }
}
#define LAST_WORDS ((volatile uint32_t *)BKPSRAM_BASE)
void last_words(uint32_t counter, uint32_t reason)
{
LAST_WORDS[1] = counter; /* запись в ОЗУ — такты, не миллисекунды */
LAST_WORDS[2] = reason;
LAST_WORDS[0] = 0x5AFEC0DEu; /* метка — последней */
}
Дальше: раздел 173б «Li-ion на плате: заряд, защита и сколько осталось» — как плату заряжать от одной литиевой ячейки, беречь её и знать, сколько осталось.