Embedded HandbookПриложения
Все разделы › XX. Элементы и типовые схемы вокруг МК
STM32F103, STM32F407 · уровень средний · 15 мин чтения
Открыть в интерактивном справочнике

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-й миллисекунде:

Разряд после пропадания 12 В: вход с 470 мкФ падает линейно, компаратор на 9 В срабатывает на 53-й миллисекунде; шина 3,3 В держится, пока вход выше 4,3 В, PVD на 2,78 В срабатывает на 135-й, а 2,0 В — на 148-й; после компаратора остаётся 94 мс, после PVD — 12,5 мс

Где ловить 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 Грабли


173а.8 Итоги

Полные исходники

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 на плате: заряд, защита и сколько осталось» — как плату заряжать от одной литиевой ячейки, беречь её и знать, сколько осталось.

Открыть в интерактивном справочнике