Инструменты пользователя

Инструменты сайта


prog:bug:pvd_testve8

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

Уровень 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, представлено в таблицах:

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

По таблицам видно, что сброс флага при повышении UCCMAX выставляется неправильно. При понижении UCCMAX флаг отрабатывает верно. Как будто бы есть цепь зарядки (повышения) уровня Ulim и цепь разрядки (понижения) Ulim. Цепь зарядки не дотягивает выставляемый уровень UCCMAX, но цепь разрядки работает верно. По сути ведь внутри наверняка используется компаратор, который сравнивает два значения Ucc и Ulim, по результату формируя выставления флага PVDP.

(На самом деле возможно, что это не так. Это лишь мое образное представление.)

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

Упрощенно код:

void SetLevel(uint32_t pvdLevel)
{
  if (pvdLevel < 0x1F)
    PWR->ULIMIT = pvdLevel + 1;
  PWR->ULIMIT = pvdLevel;
}

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

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

Этим кодом мы исправили первую таблицу - уровень Ulim теперь выставляется правильно, флаг PVDP отрабатывает верно.

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

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

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

Проект демонстрирующий выставление порога представлен на GitHub

В примере, стартовый уровень Ulim заведомо задается Ulim > 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;
}  
prog/bug/pvd_testve8.txt · Последние изменения: 2018/04/19 11:33 — vasco