#include "drv_clk.h"
#include "drv_misc.h"
#include "kit_debug.h"


#define EXTI_Line16      		  ((uint32_t)0x10000)
#define PWR_FLAG_PVDO             ((uint32_t)0x00000004)
#define PWR_CR_PVDE_POS           ((uint32_t)0x00000010)
#define PWR_PVDLevel_2V9          ((uint32_t)0x000000E0)

//设置优先级和使能中断
void drv_misc_set_nvic(int8_t irq_channel, uint8_t priority)
{
    KIT_ASSERT_PARAM((irq_channel > -15) && (irq_channel < 90) && (priority < 16));
    
    priority = (priority & 0x0F) << 4;
    if(irq_channel > 0)
    {
        NVIC->IP[irq_channel] = priority;
        /* Enable the Selected IRQ Channels */
        NVIC->ISER[irq_channel >> 0x05] = (uint32_t)0x01 << (irq_channel & (uint8_t)0x1F);
    }
    else
    {
        SCB->SHP[((uint32_t)(irq_channel) & 0xF)-4] = priority;
    }
}


//只复位内核，外围设备状态不变
void drv_misc_reset_core(void)
{
    __set_FAULTMASK(1); // 关闭所有中断
    SCB->AIRCR = ((0x5FA << SCB_AIRCR_VECTKEY_Pos) |         
                 (SCB->AIRCR & SCB_AIRCR_PRIGROUP_Msk) |     /* Keep priority group unchanged */
                 SCB_AIRCR_SYSRESETREQ_Msk);                   
    __DSB();                                                 /* Ensure completion of memory access */                 
    while(1);                                                /* wait until reset */    
}

//复位内核及MCU所有外设
void drv_misc_reset_mcu(void)
{
    __set_FAULTMASK(1); // 关闭所有中断
    NVIC_SystemReset(); // 复位
}

NoArgFuncCall pwr_off_call;
void drv_misc_cfg_pwr_off_interrupt(uint8_t priority, PwrOffVolt volt, NoArgFuncCall int_call)
{    
    KIT_ASSERT_PARAM(int_call != NULL);
    
    if(int_call != NULL)
    {
        pwr_off_call = int_call;
        drv_misc_set_nvic(PVD_IRQn, priority);
        RCC->APB1ENR |= RCC_APB1Periph_PWR;
        PWR->CR |= ((volt << 5) | PWR_CR_PVDE_POS);
        //clears the pending bits
        EXTI->PR |= EXTI_Line16;    
        EXTI->RTSR |= EXTI_Line16; //上升沿促发，PVDO输出与上下电相反
        EXTI->IMR |= EXTI_Line16;
    }
}


void PVD_IRQHandler(void)
{
    EXTI->PR |= EXTI_Line16;    
    //VDD/VDDA低于由PLS[2:0]选定的PVD阀值
    if((PWR->CSR & PWR_FLAG_PVDO) != 0)
    {
        __disable_irq();
        //关闭时钟减小功耗
        RCC->APB2ENR = 0;    
        RCC->APB1ENR = 0;
        pwr_off_call();

//        KIT_DEBUG_PRINTF("%s\r\n", "Poweroff");
    }
}

