// 外部中断模块
// 杭州秒寻科技有限公司
// 周涛
// 2023-11-14

#include "includes.h"

// 定义模块全局变量
static uint8 gExti_byLedFlag = 0;          // led亮灯标识符
static uint8 gExti_bySyncSendCounter = 0;  // 同步信号发送计数器
static uint8 gExti_bySyncTimerEnabled = 1; // 同步定时使能，0：无效，1：有效

// 外部中断模块初始化
void exti_init()
{
    exti_init_rx_pps1(); // 初始化接收同步PPS1信号外部中断线
    exti_init_tx_sync(); // 初始化发送同步信号外部中断线

    // 清除3个中断标识位
    EXTI_ClearITPendingBit(EXTI_Line0);
    EXTI_ClearITPendingBit(EXTI_Line4);
    sync_rxtx_update_tx_cplt_flag(2);
}

// 初始化exti0（接收1号北斗PPS信号）
void exti_init_rx_pps1()
{
    GPIO_InitTypeDef stGpio = {0};
    EXTI_InitTypeDef stExti = {0};
    NVIC_InitTypeDef stNvic = {0};

    // 使能SYSCOFG时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE);

    // 初始化GPIO管脚
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);
    stGpio.GPIO_Mode = GPIO_Mode_IN; // 管脚模式，输入模式
    // stGpio.GPIO_OType = GPIO_OType_OD; // 管脚输出类型，在输入模式下可以不配置
    stGpio.GPIO_PuPd = GPIO_PuPd_DOWN; // 下拉，平时保持低电平
    stGpio.GPIO_Speed = GPIO_Speed_50MHz;
    stGpio.GPIO_Pin = GPIO_Pin_0;
    GPIO_Init(GPIOB, &stGpio);

    // 配置GPIO管脚与exti线间的映射关系
    SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOB, EXTI_PinSource0);

    // 配置外部中断参数
    stExti.EXTI_Line = EXTI_Line0;
    stExti.EXTI_Mode = EXTI_Mode_Interrupt;
    stExti.EXTI_Trigger = EXTI_Trigger_Rising;
    stExti.EXTI_LineCmd = ENABLE;
    EXTI_DeInit();
    EXTI_Init(&stExti);

    // 配置中断
    stNvic.NVIC_IRQChannel = EXTI0_IRQn;
    stNvic.NVIC_IRQChannelPreemptionPriority = 0;
    stNvic.NVIC_IRQChannelSubPriority = 0;
    stNvic.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&stNvic);
}

// EXTI0中断服务程序(收到1号北斗的PPS信号)
// TODO 若PPS信号有丢失，该怎么处理呢？
void EXTI0_IRQHandler(void)
{
    uint8 byIntLevelFlag = 0;     // 中断脉冲电平值
    uint8 byTxCpltFlag = 0;       // 同步信号发送完成标识符
    uint8 byFlag = 0;             // 同步信号发送结果标识符
    uint8 byRtcTimeValidFlag = 0; // rtc时间有效标识符

    if (EXTI_GetITStatus(EXTI_Line0) != RESET)
    {
        // 延时，以便判断脉宽是否符合要求
        byIntLevelFlag = GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_0); // 查询中断脉冲电平值
        if (byIntLevelFlag)
        { // 中断有效
            gExti_bySyncSendCounter++;
            if (gExti_bySyncSendCounter == EXTI_SYNC_SEND_COUNTER)
            {
                gExti_bySyncSendCounter = 0;

                // 查询发送标识符，0：空闲可发送；1：发送进行中；2：发送已完成
                byTxCpltFlag = sync_rxtx_query_tx_cplt_flag();

                // 模块空闲，可发送
                if (!byTxCpltFlag)
                {
                    // 同步定时有效，发送同步信号，否则不发送同步信号
                    if (gExti_bySyncTimerEnabled)
                    {
                        // 修改发送标识符，发送进行中
                        sync_rxtx_update_tx_cplt_flag(1);

                        // 发送同步信号
                        byFlag = sync_rxtx_send_signal();
                        if (byFlag) // 发送成功
                        {
                            exti_enable_sync_tx_int(); // 使能同步信号发送中断
                        }
                        // 发送失败
                        else
                        {
                            sync_rxtx_update_tx_alarm(1);     // 产生发送告警
                            sync_rxtx_update_tx_cplt_flag(2); // 修改发送标识符，发送完成
                        }
                    }
                }
            }
        }

        // 清除中断标识符
        EXTI_ClearITPendingBit(EXTI_Line0);
    }

    return;
}

// 设置同步定时使能
// byFlag:使能标识，0：无效，1：有效
void exti_set_sync_timer_enabled_flag(uint8 byFlag)
{
    gExti_bySyncTimerEnabled = byFlag;
}

/*****************************************上面是处理北斗PPS信号接收******************************************************/
/**********************************************************************************************************************/
/**********************************************************************************************************************/
/*****************************************下面是处理同步信号发送*********************************************************/

// 初始化同步信号发送完成的外部中断管脚(TX_DONE)
void exti_init_tx_sync()
{
    GPIO_InitTypeDef stGpio = {0};
    EXTI_InitTypeDef stExti = {0};
    NVIC_InitTypeDef stNvic = {0};

    // 初始化GPIO管脚
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD, ENABLE);
    stGpio.GPIO_Mode = GPIO_Mode_IN;   // 管脚模式，输入模式
    stGpio.GPIO_PuPd = GPIO_PuPd_DOWN; // 下拉，平时保持低电平
    stGpio.GPIO_Speed = GPIO_Speed_50MHz;
    stGpio.GPIO_Pin = GPIO_Pin_4;
    GPIO_Init(GPIOD, &stGpio);

    // 配置GPIO管脚与exti线间的映射关系
    SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOD, EXTI_PinSource4);

    // 配置外部中断参数
    stExti.EXTI_Line = EXTI_Line4;
    stExti.EXTI_Mode = EXTI_Mode_Interrupt;
    stExti.EXTI_Trigger = EXTI_Trigger_Rising;
    stExti.EXTI_LineCmd = DISABLE;
    EXTI_Init(&stExti);

    // 配置中断
    stNvic.NVIC_IRQChannel = EXTI4_IRQn;
    stNvic.NVIC_IRQChannelPreemptionPriority = 0;
    stNvic.NVIC_IRQChannelSubPriority = 1;
    stNvic.NVIC_IRQChannelCmd = DISABLE;
    NVIC_Init(&stNvic);
}

// EXTI4的中断服务程序(同步信号发送完成外部中断线TX_DONE)
void EXTI4_IRQHandler(void)
{
    if (EXTI_GetITStatus(EXTI_Line4) != RESET)
    {
        // 信号发送已完成
        sync_rxtx_update_tx_cplt_flag(2);

        // 关闭同步信号发送中断
        exti_disable_sync_tx_int();

        // 亮灯
        gExti_byLedFlag = ~gExti_byLedFlag;
        driver_led_switch_green(gExti_byLedFlag);

        // 清除中断标识符
        EXTI_ClearITPendingBit(EXTI_Line4);
    }
}

// 使能同步信号发射中断
void exti_enable_sync_tx_int()
{
    EXTI_InitTypeDef stExti = {0};
    NVIC_InitTypeDef stNvic = {0};

    // 配置外部中断参数
    SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOD, EXTI_PinSource4); // 配置GPIO管脚与exti线间的映射关系
    stExti.EXTI_Line = EXTI_Line4;
    stExti.EXTI_Mode = EXTI_Mode_Interrupt;
    stExti.EXTI_Trigger = EXTI_Trigger_Rising;
    stExti.EXTI_LineCmd = ENABLE;
    EXTI_Init(&stExti);

    stNvic.NVIC_IRQChannel = EXTI4_IRQn;
    stNvic.NVIC_IRQChannelPreemptionPriority = 0;
    stNvic.NVIC_IRQChannelSubPriority = 1;
    stNvic.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&stNvic);

    return;
}

// 关闭同步信号发射中断
void exti_disable_sync_tx_int()
{
    EXTI_InitTypeDef stExti = {0};
    NVIC_InitTypeDef stNvic = {0};

    // 配置外部中断参数
    stExti.EXTI_Line = EXTI_Line4;
    stExti.EXTI_Mode = EXTI_Mode_Interrupt;
    stExti.EXTI_Trigger = EXTI_Trigger_Rising;
    stExti.EXTI_LineCmd = DISABLE;
    EXTI_Init(&stExti);

    stNvic.NVIC_IRQChannel = EXTI4_IRQn;
    stNvic.NVIC_IRQChannelPreemptionPriority = 0;
    stNvic.NVIC_IRQChannelSubPriority = 1;
    stNvic.NVIC_IRQChannelCmd = DISABLE;
    NVIC_Init(&stNvic);

    return;
}