24280

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

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

Уровень ULIMIT.UCCMAX детектора питания Power микроконтроллера 1986ВЕ8Т выставляется некорректно. Работа блока Power аналогична упомянутой в статье - Особенности детектора питания, блок Power для 1986ВЕ1Т и 1986ВЕ9х. Имеющийся здесь параметр ULIMIT.UCCMAX соответствует параметру LevelPVD из упомянутой статьи, флаг PVDP аналогичен PVD, остальные понятия также аналогичны. Различия есть, но при ознакомлении со статьёй интуитивно понятны. Дополнительное описание работы блока детектора питания приведено не будет.

Проблема заключается в следующем: при напряжении питания на демонстрационной плате 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

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

Документация

Теги

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