Embedded HandbookПриложения
Все разделы › III. Периферия
STM32F103, STM32F334, STM32G474, STM32L476 · уровень продвинутый · 15 мин чтения
Открыть в интерактивном справочнике

46. Таймеры специального назначения: HRTIM и LPTIM

Два таймера с противоположными целями. HRTIM отмеряет сотни пикосекунд — столько нужно, чтобы держать выходное напряжение импульсного преобразователя с точностью до долей милливольта. LPTIM работает от 32 768 Гц и потребляет доли микроампера — столько нужно, чтобы будить устройство раз в минуту год подряд от одной батарейки. Общее у них только слово «таймер»: ни тот ни другой не заменяет обычный, оба решают задачу, где обычного не хватает.

Опорные камни: G474 (HRTIM второй версии и LPTIM), F334 (HRTIM первой версии), L476 (LPTIM). У F103 и F407 нет ни того ни другого.

Как это называется в документации

Оригинал Перевод
high-resolution timer (HRTIM) таймер высокого разрешения
low-power timer (LPTIM) таймер с низким потреблением
delay-locked loop (DLL) линия задержки: она и даёт доли наносекунды
prescaler ratio, multiplier делитель и умножитель частоты счёта
duty cycle коэффициент заполнения, «скважность» наоборот
dead time защитный интервал: оба ключа закрыты
shoot-through current сквозной ток через оба ключа полумоста
complementary outputs комплементарная пара выходов
break / fault input аварийный вход
burst mode пакетный режим: ШИМ пачками, между ними пауза
push-pull mode двухтактный режим
one-shot / continuous одиночный запуск / непрерывный счёт
autoreload register (ARR) регистр перезагрузки: задаёт период
pulse counter счётчик импульсов
glitch filter фильтр коротких помех на входе

46.1 Зачем нужен ещё один таймер

Обычный таймер на 170 МГц при ШИМ 100 кГц укладывает в период 1700 шагов. Кажется, много — но у понижающего преобразователя 12 → 5 В шаг заполнения превращается в 7 мВ на выходе, а ближайшее к нужному заполнение промахивается на 2,4 мВ. Для светодиода это неважно, для стабилизатора напряжения — уже заметно.

HRTIM умножает частоту счёта на 32: 5,44 ГГц, шаг 184 пс, 54 400 шагов в том же периоде. Промах — 0,07 мВ.

Обычный таймер HRTIM (×32)
частота счёта при 170 МГц 170 МГц 5,44 ГГц
шаг 5,9 нс 184 пс
шагов в периоде 100 кГц 1700 54 400
шаг заполнения на выходе 12 В 7,06 мВ 0,22 мВ
ошибка установки 5 В 2,4 мВ 0,07 мВ

Вторая причина — не разрешение, а гибкость: у HRTIM события «включить выход» и «выключить выход» задаются независимо и выбираются из большого набора (период, четыре сравнения, внешние события, выходы других таймеров). Отсюда фазовый сдвиг, двухтактные схемы и управление четырьмя ключами двумя каналами — без единой команды процессора внутри периода.


46.2 Устройство HRTIM

Внутри — один ведущий счётчик и шесть независимых таймеров A…F (у первой версии, F334, — пять, A…E). У каждого свои период, четыре регистра сравнения, два выхода, свой защитный интервал и своя привязка к аварийным входам. Ведущий задаёт общий период и синхронизирует остальные.

/* Число таймеров различает версии блока. */
_Static_assert(sizeof(((HRTIM_TypeDef *)0)->sTimerxRegs) / sizeof(HRTIM_Timerx_TypeDef) == TIMERS_IN_HRTIM,
               "число независимых таймеров внутри HRTIM");

Частота счёта. Поле CK_PSC — не только делитель: младшие коды умножают частоту.

Код Что делает При 170 МГц Шаг Диапазон частот ШИМ
0 ×32 5,44 ГГц 184 пс 83 кГц … 57 МГц
1…4 ×16 … ×2 2,72 … 0,34 ГГц 0,37 … 2,9 нс ниже
5 как есть 170 МГц 5,9 нс 2,6 кГц … 57 МГц (на 3,5 МГц в периоде лишь 48 шагов)
6, 7 ÷2, ÷4 85, 42,5 МГц 12, 24 нс от 649 Гц

Ограничение сверху и снизу — из тех же 16 бит счётчика: при ×32 период 65535 шагов кончается на 83 кГц, ниже нужен меньший умножитель. Снизу период не должен быть короче трёх тактов исходной частоты — это 0x0060 при ×32, то есть около 57 МГц. Эта граница у всех кодов одна: меньший умножитель только огрубляет шаг.

Калибровка. Доли наносекунды даёт линия задержки, а её задержка зависит от температуры и питания. Поэтому перед работой её калибруют, и дальше калибровку повторяют периодически:

HRTIM1->sCommonRegs.DLLCR = HRTIM_DLLCR_CAL | HRTIM_DLLCR_CALEN;
while ((HRTIM1->sCommonRegs.ISR & HRTIM_ISR_DLLRDY) == 0u) { }

Без этого шага выходы работают, но обещанного разрешения нет — типовая ошибка при первом запуске.


46.3 🔧 Понижающий преобразователь: ШИМ, пара ключей, авария

ШИМ высокого разрешения и защитный интервал

Задача: 12 В на входе, 5 В на выходе, 100 кГц, синхронный выпрямитель — вместо диода второй ключ, значит нужна комплементарная пара с защитным интервалом.

Период: 5,44 ГГц / 100 кГц = 54 400. Сравнение: 54 400 × 5/12 = 22 667.

#define TIMER_D   3u
static void hrtim_buck_setup(void)
{
    HRTIM_Timerx_TypeDef *td = &HRTIM1->sTimerxRegs[TIMER_D];

    RCC->APB2ENR |= RCC_APB2ENR_HRTIM1EN;

    /* Без готовой линии задержки разрешение не соблюдается. */
    HRTIM1->sCommonRegs.DLLCR = HRTIM_DLLCR_CAL | HRTIM_DLLCR_CALEN;
    while ((HRTIM1->sCommonRegs.ISR & HRTIM_ISR_DLLRDY) == 0u) { }

    td->TIMxCR = (CKPSC_MUL32 << HRTIM_TIMCR_CK_PSC_Pos)
               | HRTIM_TIMCR_CONT                     /* считать непрерывно */
               | HRTIM_TIMCR_PREEN                    /* период и сравнение — через тень */
               | HRTIM_TIMCR_TRSTU;                   /* тень → рабочий в начале каждого периода */
    td->PERxR  = PWM_PER;
    td->CMP1xR = PWM_CMP;
    HRTIM1->sCommonRegs.CR2 |= HRTIM_CR2_TDSWU;       /* первый перенос — программно */

    td->SETx1R = HRTIM_SET1R_PER;                     /* верхний ключ включается в начале периода */
    td->RSTx1R = HRTIM_RST1R_CMP1;                    /* и выключается по сравнению */

    /* Защитный интервал: второй выход получается сам, инверсией с задержкой. */
    td->DTxR   = (DT_STEPS << HRTIM_DTR_DTR_Pos)
               | (DT_STEPS << HRTIM_DTR_DTF_Pos);
    td->OUTxR  = HRTIM_OUTR_DTEN
               | (FAULT_HIGHZ << HRTIM_OUTR_FAULT1_Pos);

    /* Авария: вход FLT1 с фильтром, дальше — замок до сброса. */
    HRTIM1->sCommonRegs.FLTINR1 = HRTIM_FLTINR1_FLT1E
                                | (5u << HRTIM_FLTINR1_FLT1F_Pos)
                                | HRTIM_FLTINR1_FLT1LCK;
    td->FLTxR  = HRTIM_FLTR_FLT1EN | HRTIM_FLTR_FLTLCK;

    HRTIM1->sCommonRegs.OENR = HRTIM_OENR_TD1OEN | HRTIM_OENR_TD2OEN;
    HRTIM1->sMasterRegs.MCR |= HRTIM_MCR_TDCEN;       /* пуск */
}

Смена напряжения на ходу — одна запись: теневой регистр применится в начале следующего периода, посреди импульса форма не порвётся. Тень работает, только если разрешён источник переноса: здесь это TRSTU — начало каждого периода, а первый перенос делает программа битом TDSWU в CR2. Без них рабочие регистры так и остаются со значениями после сброса.

static void hrtim_set_duty(uint32_t vout_mv)
{
    HRTIM1->sTimerxRegs[TIMER_D].CMP1xR = (uint32_t)(((uint64_t)PWM_PER * vout_mv + VIN_MV / 2u) / VIN_MV);
}

Защитный интервал. Когда DTEN включён, второй выход перестаёт быть самостоятельным: он становится инверсией первого, сдвинутой на заданное время. Задержки на включение и на выключение задаются отдельно (по 9 бит), генератор интервала тактируется от fHRTIM × 8 = 1,36 ГГц: шаг 0,735 нс, максимум 376 нс. Типовые 100 нс — это 136 шагов и ровно один процент периода: столько заполнения теряется, и это надо учитывать в регуляторе.

Зачем интервал: ключи полумоста закрываются не мгновенно. Если нижний откроется раньше, чем закрылся верхний, через оба потечёт сквозной ток — десятки ампер напрямую с шины питания на землю. Это не «немного греется», это разрушение ключей за микросекунды.

Авария. Выход компаратора или внешний сигнал по аварийному входу снимает ШИМ аппаратно, без ядра (раздел 44.7). Что делать с выходом, задаёт поле FAULT1: держать активным, снять, отпустить в высокоимпедансное состояние. Биты FLTLCK и FLT1LCK замораживают настройку до сброса — защиту, которую нельзя случайно выключить.


46.4 Что HRTIM умеет ещё

Фазовый сдвиг. Два таймера с общим периодом и сравнением, смещённым на половину периода (27 200 шагов), дают два ШИМ в противофазе. Шаг фазы — 0,0066°. Так строят двухфазные преобразователи: ток делится между двумя дросселями, пульсации на выходе гасятся взаимно.

Понижающе-повышающий преобразователь. Четыре ключа, два таймера: один работает понижающим плечом, второй повышающим. Схема переключается между режимами на ходу, в зависимости от того, выше входное напряжение выходного или ниже. Оба плеча — с защитным интервалом, оба слушают аварийный вход.

Пакетный режим. При малой нагрузке импульсный преобразователь эффективнее работает пачками: несколько периодов ШИМ, потом пауза. Блок делает это сам, регистрами BMCR, BMPER, BMCMPR.

Запуск АЦП. HRTIM выдаёт сигналы запуска преобразования, привязанные к нужному месту периода: ток дросселя измеряют в середине импульса, где он не искажён переключением (раздел 14.5).

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


46.5 LPTIM: таймер, который работает во сне

Обратная задача. Устройство спит в режиме Stop; всё, что тактируется системной частотой, стоит. Обычный таймер стоит тоже. LPTIM — нет: он берёт такты от LSE, LSI или прямо с внешнего вывода и продолжает считать.

TIM2 LPTIM1
разрядность 32 бита 16 бит
частота до 170 МГц 32 768 Гц (LSE)
работает в Stop нет да
потребление 1843 мкА на 170 МГц 0,14 мкА на LSE
каналов 4 1

Разница в потреблении — почти в 13 тысяч раз. Правда, тут же и трезвая поправка: на фоне тока режима Stop 1 у G4 (около 60 мкА при комнатной температуре) таймер добавляет 0,24 % — то есть не таймер определяет расход батареи, а режим сна и частота пробуждений. Сам выход из Stop — 1,39 мА на 20 мкс — раз в секунду среднего тока не меняет, сто раз в секунду даёт плюс 5 %. Но после выхода ядро работает, и эту работу считают так же: ток рабочего режима на той частоте, на которой ядро работает после пробуждения, умноженный на долю времени бодрствования. G431 на 170 МГц берёт 25,5 мА: 20 мкс работы раз в секунду добавляют 0,5 мкА, сто раз в секунду — 51 мкА, почти вдвое к току сна.

Да и сам Stop 1 у G4 для батарейки великоват: год от таблетки 220 мА·ч — это средний ток не больше 25 мкА, а при 59,5 мкА сна её хватит на 154 суток. Батарейные семейства спят глубже: у L476 режим Stop 2 с часами на LSE берёт 1,67 мкА, и десятая доля микроампера самого LPTIM там — уже около 6 %.

Токи TIM2, режима Stop и LPTIM; средний ток в Stop в зависимости от частоты пробуждений

Три применения.

Первое — будильник. Просыпаться раз в секунду, измерить, уснуть:

#define WAKE_S    1u
static void lptim_wakeup_1s(void)
{
    RCC->APB1ENR1 |= RCC_APB1ENR1_LPTIM1EN;
    RCC->CCIPR = (RCC->CCIPR & ~RCC_CCIPR_LPTIM1SEL_Msk)
               | (LPTIM1SEL_LSE << RCC_CCIPR_LPTIM1SEL_Pos);

    LPTIM1->CFGR = 0u;                       /* делитель 1, такты внутренние */
    LPTIM1->CR   = LPTIM_CR_ENABLE;          /* ARR пишут только у включённого блока */
    LPTIM1->ARR  = LSE_HZ * WAKE_S - 1u;     /* период = ARR + 1 отсчётов */
    while ((LPTIM1->ISR & LPTIM_ISR_ARROK) == 0u) { }   /* дождаться переноса в другой домен */
    LPTIM1->ICR  = LPTIM_ICR_ARROKCF;
    LPTIM1->IER  = LPTIM_IER_ARRMIE;
    LPTIM1->CR  |= LPTIM_CR_CNTSTRT;
    NVIC_EnableIRQ(LPTIM1_IRQn);
}

Пределы: 16 бит от LSE — 2 секунды; с делителем 128 — 256 секунд. Дольше — считать пробуждения в переменной либо брать RTC (раздел 12.6).

Второе — ШИМ, который не гаснет во сне. Подсветка, зуммер, подогрев: ядро спит, скважность держится.

static void lptim_pwm_in_stop(void)
{
    LPTIM1->CFGR = LPTIM_CFGR_PRELOAD;       /* обновлять ARR и CMP по концу периода */
    LPTIM1->CR   = LPTIM_CR_ENABLE;
    LPTIM1->ARR  = PWM_ARR;
    while ((LPTIM1->ISR & LPTIM_ISR_ARROK) == 0u) { }
    LPTIM1->CMP  = PWM_CMP_Q;
    while ((LPTIM1->ISR & LPTIM_ISR_CMPOK) == 0u) { }
    LPTIM1->ICR  = LPTIM_ICR_ARROKCF | LPTIM_ICR_CMPOKCF;
    LPTIM1->CR  |= LPTIM_CR_CNTSTRT;
}

ARR = 255 от LSE даёт 128 Гц и шаг заполнения 0,39 % — грубо по меркам обычного таймера, но во сне.

Третье — счётчик импульсов. COUNTMODE переключает блок на счёт фронтов с вывода, а такты LSE нужны только цифровому фильтру: он сравнивает отсчёты входа и без внутренних тактов не работает. Если фильтр не нужен, блок можно тактировать прямо фронтами входа (CKSEL = 1, CKFLT = 0) — тогда не работает ни один генератор, а дребезг гасят RC-цепью (раздел 50.7). Расходомер, турбина, геркон на двери — считают годами.

static void lptim_pulse_counter(void)
{
    RCC->CCIPR = (RCC->CCIPR & ~RCC_CCIPR_LPTIM1SEL_Msk)
               | (LPTIM1SEL_LSE << RCC_CCIPR_LPTIM1SEL_Pos);   /* окно фильтра отсчитывает LSE */
    LPTIM1->CFGR = LPTIM_CFGR_COUNTMODE                        /* фронты входа, CKSEL = 0 */
                 | (3u << LPTIM_CFGR_CKFLT_Pos);               /* фильтр от дребезга: 8 тактов LSE */
    LPTIM1->CR   = LPTIM_CR_ENABLE;
    LPTIM1->ARR  = 0xFFFFu;
    while ((LPTIM1->ISR & LPTIM_ISR_ARROK) == 0u) { }
    LPTIM1->CR  |= LPTIM_CR_CNTSTRT;
}

Есть и режим энкодера (ENC), и режим тайм-аута (TIMOUT), где внешнее событие перезапускает счёт: удобно ловить «сигнал пропал дольше чем на».


46.6 Два правила LPTIM, о которые спотыкаются все

Регистры живут в другом домене тактов. Ядро пишет ARR и CMP на системной частоте, а работают они на 32 768 Гц. Перенос занимает несколько тактов медленной частоты и подтверждается флагами ARROK и CMPOK. Записать второй раз, не дождавшись флага, — потерять значение. И писать их можно только у включённого блока (ENABLE уже стоит).

Счётчик нельзя читать одним чтением. Он тикает от чужих тактов, и чтение может застать момент переноса — получится значение, которого не было. Читают, пока два подряд не совпадут:

static uint32_t lptim_read_count(void)
{
    uint32_t a, b;
    do {
        a = LPTIM1->CNT;
        b = LPTIM1->CNT;
    } while (a != b);
    return a & LPTIM_CNT_CNT_Msk;
}

Та же осторожность нужна везде, где регистр обновляется независимо от процессора, — это общее свойство, а не причуда LPTIM.


46.7 HAL

Функция Что делает
HAL_HRTIM_Init, HAL_HRTIM_DLLCalibrationStart базовая настройка и калибровка линии задержки
HAL_HRTIM_TimeBaseConfig умножитель, период, режим счёта
HAL_HRTIM_WaveformTimerConfig, HAL_HRTIM_WaveformOutputConfig что задаёт форму: события включения и выключения, полярность, поведение по аварии
HAL_HRTIM_WaveformCompareConfig регистры сравнения
HAL_HRTIM_TimerEventFilteringConfig, HAL_HRTIM_FaultConfig внешние события и аварийные входы
HAL_HRTIM_WaveformOutputStart, HAL_HRTIM_WaveformCountStart включить выходы, запустить счёт
HAL_HRTIM_BurstModeConfig пакетный режим
HAL_LPTIM_Init источник тактов, делитель, запуск, фильтры
HAL_LPTIM_PWM_Start, HAL_LPTIM_OnePulse_Start, HAL_LPTIM_TimeOut_Start_IT режимы выхода
HAL_LPTIM_Counter_Start_IT, HAL_LPTIM_Encoder_Start счётчик импульсов, энкодер
HAL_LPTIM_ReadCounter чтение счётчика

Настройку HRTIM почти всегда делают генератором проекта: у блока сотни полей, и связная картина в графическом виде разбирается быстрее, чем в таблице регистров. Но понимать, что именно сгенерировано, всё равно приходится — иначе не отладить.


46.8 Грабли

HRTIM: разрешение не то, что обещано. Не сделана калибровка линии задержки или не дождались DLLRDY.

HRTIM: частота ниже 83 кГц не выставляется. При ×32 период не помещается в 16 бит. Меньший умножитель — грубее шаг, это цена.

HRTIM: выходы молчат. Не разрешены в OENR, либо выводы TD1/TD2 и FLT1 не переведены в альтернативную функцию HRTIM (номер — по таблице выводов корпуса), либо не запущен счёт в MCR, либо блок держится в аварии — сначала снять её причину.

HRTIM: ключи греются, схема иногда разлетается. Нет защитного интервала или он короче времени выключения ключа. Проверять по даташиту ключа и драйвера, с запасом.

HRTIM: заполнение «прыгает» при изменении на ходу. Не включены теневые регистры (PREEN) — новое значение применилось посреди импульса.

HRTIM: новые период и сравнение не применяются. PREEN включён, а источник переноса из тени — нет: ни TRSTU (по периоду), ни TREPU (по повторению), ни программный бит в CR2.

F334 против G4. У первой версии пять таймеров вместо шести и другой источник тактов: там он выбирается в RCC (удвоенная частота PLL либо шинная), у G4 таймер тактируется системной частотой.

LPTIM: запись в ARR не действует. Блок не включён или не дождались ARROK.

LPTIM: счётчик показывает чушь. Одно чтение вместо двух совпавших.

LPTIM: не будит из Stop. Источник тактов — шинная частота, а не LSE или LSI: в Stop она стоит.

LPTIM: считает лишние импульсы. Не включён фильтр (CKFLT), или контакт дребезжит сильнее его окна. Фильтр работает только от внутренних тактов: с CKSEL = 1 его нет, дребезг гасят RC-цепью.

Батарея садится быстрее расчётного. Дело почти никогда не в таймере: смотреть частоту пробуждений, время работы после пробуждения и периферию, забытую включённой.


46.9 Итоги

Дальше: раздел 46а «CORDIC: синус, косинус и фаза за несколько тактов» — сопроцессор G4 для тригонометрии в управлении двигателем.

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