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 %.
Три применения.
Первое — будильник. Просыпаться раз в секунду, измерить, уснуть:
#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 Итоги
- HRTIM умножает частоту счёта до 5,44 ГГц: шаг 184 пс, в 32 раза мельче обычного таймера; ошибка установки напряжения падает с милливольт до десятых долей.
- Шесть независимых таймеров (у первой версии — пять), у каждого события включения и выключения выхода назначаются раздельно — отсюда фазовый сдвиг, двухтактные и мостовые схемы.
- Защитный интервал — шаг 0,735 нс, до 376 нс; без него полумост живёт недолго.
- Аварийный вход снимает ШИМ аппаратно, настройку можно заморозить до сброса.
- Линию задержки калибруют перед работой и периодически — иначе разрешения нет.
- LPTIM считает от LSE, LSI или внешнего вывода и работает в Stop: будильник, ШИМ во сне, счётчик импульсов.
ARRиCMPпишут у включённого блока и ждутARROK/CMPOK; счётчик читают дважды до совпадения.- Потребление самого LPTIM — доли микроампера; расход батареи задаёт не он, а режим сна, частота пробуждений и работа после них. Stop 1 у G4 (59,5 мкА) держит таблетку 154 суток; на год нужен сон семейства вроде L4 — Stop 2 с часами у L476 берёт 1,67 мкА.
Дальше: раздел 46а «CORDIC: синус, косинус и фаза за несколько тактов» — сопроцессор G4 для тригонометрии в управлении двигателем.