// 外部中断模块
// 杭州秒寻科技有限公司
// 周涛
// 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_pps_bd(); // 初始化北斗PPS信号外部中断，
    exti_init_pps_cpld(); // 初始化CPLD的PPS信号外部中断
    exti_init_tx_sync();  // 初始化同步信号发送外部中断

    // 清除4个中断标识位
    // EXTI_ClearITPendingBit(EXTI_BD_LINE);
    EXTI_ClearITPendingBit(EXTI_CPLD_LINE);
    EXTI_ClearITPendingBit(EXTI_SYNC_LINE);
    sync_rxtx_update_tx_cplt_flag(2);
}

// 初始化外部中断（北斗PPS信号）
void exti_init_pps_bd()
{
    GPIO_InitTypeDef stGpio = {0};
    EXTI_InitTypeDef stExti = {0};
    NVIC_InitTypeDef stNvic = {0};

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

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

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

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

    // 配置中断
    stNvic.NVIC_IRQChannel = EXTI_BD_IRQ_CHANNEL;
    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_BD_LINE) != RESET)
    {
        // 延时，以便判断脉宽是否符合要求
        byIntLevelFlag = GPIO_ReadInputDataBit(EXTI_BD_GPIO_PORT, EXTI_BD_GPIO_PIN); // 查询中断脉冲电平值
        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); // 修改发送标识符，发送完成
                        }
                    }
                }
            }

            // 同步定时无效，不发送同步信号，指示灯每秒闪烁一次
            if (!gExti_bySyncTimerEnabled)
            {
                // 亮灯
                gExti_byLedFlag ^= 1; // 异或操作
                driver_led_switch_green(gExti_byLedFlag);
            }
        }

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

    return;
}

// 初始化外部中断（cpld的PPS信号）
void exti_init_pps_cpld()
{
    GPIO_InitTypeDef stGpio = {0};
    EXTI_InitTypeDef stExti = {0};
    NVIC_InitTypeDef stNvic = {0};

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

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

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

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

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

// EXTI1中断服务程序(收到CPLD的PPS信号)
void EXTI1_IRQHandler(void)
{
    uint8 byIntLevelFlag = 0;     // 中断脉冲电平值
    uint8 byTxCpltFlag = 0;       // 同步信号发送完成标识符
    uint8 byFlag = 0;             // 同步信号发送结果标识符
    uint8 byRtcTimeValidFlag = 0; // rtc时间有效标识符

    if (EXTI_GetITStatus(EXTI_CPLD_LINE) != RESET)
    {
        // 延时，以便判断脉宽是否符合要求
        byIntLevelFlag = GPIO_ReadInputDataBit(EXTI_CPLD_GPIO_PORT, EXTI_CPLD_GPIO_PIN); // 查询中断脉冲电平值
        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); // 修改发送标识符，发送完成
                        }
                    }
                }
            }

            // 同步定时无效，不发送同步信号，指示灯每秒闪烁一次
            if (!gExti_bySyncTimerEnabled)
            {
                // 亮灯
                gExti_byLedFlag ^= 1; // 异或操作
                driver_led_switch_green(gExti_byLedFlag);
            }
        }

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

    return;
}

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

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

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

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

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

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

    // 配置中断
    stNvic.NVIC_IRQChannel = EXTI_SYNC_IRQ_CHANNEL;
    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_SYNC_LINE) != RESET)
    {
        // 信号发送已完成
        sync_rxtx_update_tx_cplt_flag(2);

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

        // 亮灯
        gExti_byLedFlag ^= 1; // 异或操作
        driver_led_switch_green(gExti_byLedFlag);

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

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

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

    stNvic.NVIC_IRQChannel = EXTI_SYNC_IRQ_CHANNEL;
    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_SYNC_LINE;
    stExti.EXTI_Mode = EXTI_Mode_Interrupt;
    stExti.EXTI_Trigger = EXTI_Trigger_Rising;
    stExti.EXTI_LineCmd = DISABLE;
    EXTI_Init(&stExti);

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

    return;
}