24280

[i] Особенности детектора питания. Блок Power в К1986ВЕ8T

Автор статьи: Лампадов Илья Александрович (Инженер)
Дата последнего изменения: 02.12.2025 18:04:48

Уровень ULIMIT.UCCMAX детектора питания Power микроконтроллера 1986ВЕ8Т выставляется некорректно. 

Проблема заключается в следующем: при напряжении питания на демонстрационной плате Ucc = 3,3В сброс флага PVDP должен происходить при выставлении UCCMAX = 0хВ, что соответствует порогу 3,4В, но сброс PVDP происходит только при UCCMAX = 0хС, то есть, при LevelPVD = 3,6В. При этом заявленная точность срабатывания детектора питания составляет 100мВ.

Выяснилось, что такое неточное выставление порога в UCCMAX происходит, только если в UCCMAX ранее был выставлен более низкий порог. Если UCCMAX содержал более высокое значение, то выставление порога происходит корректно, то есть, детектор питания зафиксирует событие Ucc > Ulim при значении UCCMAX = 0хВ (3,4В).

Термином Ulim далее будет называться уровень напряжения (порог), который задается параметром UCCMAX в регистре ULIMIT. Это напряжение Ulim сравнивается с Ucc и по результатам сравнения выставляется флага PVDP в регистре STAT.

Более наглядно выставление флага PVDP при сравнении Ucc (3,3В) c последовательно изменяемым значением UCCMAX, а следовательно и порогом Ulim, представлено в таблицах ниже:

Таблица 1 - Выставление флага PVDP в зависимости от увеличения UCCMAX

Увеличиваем UCCMAX 0xA 0xB 0xC
Ulim, В 3,2 3,4 3,6
Флаг STAT.PVDP 1 1 0

Таблица 2 - Выставление флага PVDP в зависимости от уменьшения UCCMAX
Уменьшаем UCCMAX 0xС 0xB 0xA
Ulim, В 3,6 3,4 3,2
Флаг STAT.PVDP 0 0 1

В итоге, поскольку понижение уровня дает верное поведение PVDP, можно попробовать задать упреждающее большее значение UCCMAX при задании Ulim следующим упрощённым кодом:

void SetLevel(uint32_t pvdLevel) { if (pvdLevel < 0x1F) PWR->ULIMIT = pvdLevel + 1; PWR->ULIMIT = pvdLevel; } Фрагмент кода 1

Это позволило выставить уровень сравнения в 3.4В, при этом флаг сбросился (STAT.PVDP = 0). Присвоение повышенного значения (pvdLevel + 1) форсирует выставление порога Ulim, цепь зарядки стартует активней, поскольку разница между текущим значением и следующим больше (аналогия с отрицательной обратной связью). Присвоение второго, правильного значения pvdLevel, выставляет порог Ulim в точно заданное значение.

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

Резюме: упрощенный код, написанный выше, исправляет первую таблицу. Теперь уровень Ulim выставляется правильно, флаг PVDP отрабатывает корректно.

Таблица 3 - Выставление флага PVDP в зависимости от увеличения UCCMAX с учётом добавочного кода

Увеличиваем UCCMAX 0xA 0xB 0xC
Ожидаемый Порог, В 3,2 3,4 3,6
Флаг STAT.PVDP 1 0 0

Вторая таблица изначально верна и сохраняет свою актуальность.

Пример выставления уровня UCCMAX

Проект демонстрирующий выставление порога представлен внизу, после статьи, раздел "Файлы для скачивания".

В примере стартовый уровень Ulim заведомо задается больше UccUlim > Ucc. Далее Ulim в цикле уменьшается до значения, при котором зафиксируется флаг PVDP, то есть устанавливается ситуация Ucc > Ulim. Этот уровень сохраняется в переменной pvdLevelUccFall и далее используется для детектирования падения напряжения - для этого включается инверсия флага PVDP.

В основном цикле выставляется условие возникновения прерывания UCCMAX = pvdLevelUccFall + 1, что вызывает прерывание от блока Power. Чтобы прерывание не возникало снова и снова, порог сбрасывается к исходному состоянию UCCMAX = pvdLevelUccFall. Все это повторяется по циклу.

Для наглядности исполнения мигает светодиод, если алгоритм отрабатывает верно.

Если не использовать дополненный код с форсированным выставлением UCCMAX, то для возникновения прерывания по PVDP необходимо уровень задавать на +1 больше, то есть в основном цикле вызывается UCCMAX = pvdLevelUccFall + 2. Пояснения следующие: pvdLevelUccFall - это максимальный уровень Ulim, при котором прерывание не генерируется (Ucc > pvdLevelUccFall). Следующий уровень уже превышает Ucc и ведет к формированию прерывания Ucc < Ulim.

Использование варианта с форсированным выставлением UCCMAX включается определением USE_PVD_HACK.

Основная часть кода представлена ниже:

#define TO_ULIM_PVD_LEVEL(Vx10) (((Vx10) - 12) / 2)

#define PVD_LEVEL_MIN TO_ULIM_PVD_LEVEL(28) // Uref = 2.8V
#define PVD_LEVEL_MAX TO_ULIM_PVD_LEVEL(38) // Uref = 3.8V

#define USE_PVD_HACK

#define LED_VD7 PORT_Pin_16

int main()
{
   POR_disable();
   // Clock
   CLKCTRL_DeInit();
   CLKCTRL_HSEconfig(CLKCTRL_HSE0_CLK_ON);
   while(CLKCTRL_HSEstatus(CLKCTRL_HSEn_STAT_HSE0_RDY) != SUCCESS){}

   CLKCTRL_MAX_CLKSelection(CLKCTRL_MAX_CLK_HSE0div1);

   // Индикация
   LED_Init();

   // 1 - PVD Init, Uref выставляем в заведомо > Ucc
   PVD_Init(PVD_LEVEL_MAX, WAIT_PVD_LEVEL_TICKS);
  
   // 2 - Понижаем Uref, пока не появится флаг PVDP - (Uref < Ucc)
   // Это значение и будет порогом срабатывания при просадке питания
   pvdLevelUccFall = PVD_SetLevelOfUccFall(PVD_LEVEL_MAX, PVD_LEVEL_MIN, PVDP_CHECK_COUNT, WAIT_PVD_LEVEL_TICKS);
   
   // 3 - Инверсия флага PVDP - теперь прерывания будут срабатывать по (Ucc < Uref)
   PWR->CNTR2 = 1;
   PWR->STAT = 1;

   // 4 - Включение прерывания по Ucc < Uref
   NVIC_EnableIRQ(PVD_IF_IRQn);
   PWR->CNTR1 = 1;

   // 5 - Ожидание прерывания 
   while (1)
   {
      // Индикация
      PORT_SetBits(PORTC, LED_VD7);
      Delay(LED_PERIOD);

      // Генерация прерывания по Ucc < Uref
   #ifdef USE_PVD_HACK
      PVD_SetLevel(pvdLevelUccFall + 1, WAIT_PVD_LEVEL_TICKS);
   #else
      // Выставляется неточно, поэтому ставится с запасом.
      // При +1 прерывание не возникает.
      PVD_SetLevel(pvdLevelUccFall + 2, WAIT_PVD_LEVEL_TICKS);
   #endif

   Delay(LED_PERIOD);
   }
}

void PVD_IF_Handler(void)
{
   // Сброс в Ucc > Uref
   if (pvdLevelUccFall != PWR->ULIMIT)
   {
      PVD_SetLevel(pvdLevelUccFall, 0);

      // Индикация на светодиод
      PORT_ResetBits(PORTC, LED_VD7);
   }
   PWR->STAT = 1;
}

Сохранить статью в PDF

Файлы для скачивания

Теги

Была ли статья полезной?