/***********************头文件****************************/
#include "main.h"
#include "sys.h"
#include "delay.h"
#include "led.h"
#include "exti.h"
#include "includes.h"
#include "timer.h"

/***********************指针变量****************************/
OS_EVENT * sem_TaskC;    //任务C信号量指针
OS_EVENT * sem_Mutex;    //互斥信号量
OS_EVENT * q_Msg;    //消息队列
void * MsgGrp[256];   //消息队列存储地址,最大支持256个消息

/***********************全局变量****************************/
u16 *I3_Num;
static u16 I3_InNum;    // 存储邮箱消息
u32 PB4_Num;    //存储按键次数
u8 flag_Exit;   //执行按键函数中断标志位
uint8_t taskA_Num = 0 ;//记录任务A小灯闪烁次数

/**********************START 任务****************************/
#define START_TASK_PRIO			3  //开始任务的优先级
#define START_STK_SIZE			128  //设置任务堆栈大小
OS_STK START_TASK_STK[START_STK_SIZE];//任务任务堆栈
void start_task(void *pdata);//任务函数

/**********************任务A*********************************/
#define LEDA_TASK_PRIO			4  //设置任务优先级
#define LEDA_STK_SIZE			128  //设置任务堆栈大小
OS_STK LEDA_TASK_STK[LEDA_STK_SIZE];  //任务堆栈
void ledA_task(void *pdata);  //任务函数

/**********************任务B********************************/
#define LEDB_TASK_PRIO			5  //设置任务优先级
#define LEDB_STK_SIZE			128  //设置任务堆栈大小
OS_STK LEDB_TASK_STK[LEDB_STK_SIZE];  //任务堆栈
void ledB_task(void *pdata);  //任务函数

/**********************任务C********************************/
#define LEDC_TASK_PRIO			6  //设置任务优先级
#define LEDC_STK_SIZE			128  //设置任务堆栈大小
OS_STK LEDC_TASK_STK[LEDC_STK_SIZE]; //任务堆栈
void ledC_task(void *pdata);  //任务函数

/**********************任务D********************************/
#define LEDD_TASK_PRIO			9  //设置任务优先级
#define LEDD_STK_SIZE			128  //设置任务堆栈大小
OS_STK LEDD_TASK_STK[LEDD_STK_SIZE];  //任务堆栈
void ledD_task(void *pdata);  //任务函数

/*********************任务E********************************/
#define LEDE_TASK_PRIO			10  //设置任务优先级
#define LEDE_STK_SIZE			128  //设置任务堆栈大小
OS_STK LEDE_TASK_STK[LEDE_STK_SIZE];  //任务堆栈
void ledE_task(void *pdata);  //任务函数

/*********************任务F ********************************/
#define LEDF_TASK_PRIO			7  //设置任务优先级
#define LEDF_STK_SIZE			128  //设置任务堆栈大小
OS_STK LEDF_TASK_STK[LEDF_STK_SIZE];  //任务堆栈
void ledF_task(void *pdata);  //任务函数 

/********************按键检测任务****************************/
#define KEYG_TASK_PRIO			11  //设置任务优先级
#define KEYG_STK_SIZE			128  //设置任务堆栈大小
OS_STK KEYG_TASK_STK[KEYG_STK_SIZE];  //任务堆栈
void keyG_task(void *pdata);  //任务函数


/********************主函数****************************/
int main(void)
{
    
    HSI_SetSysClock(16, 336, 2, 7); //使用HSI配置系统时钟为168M  
    TIM3_Int_Init(4999, 8399);  //时钟初始化
	delay_init(168);       //延时初始化
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //中断分组配置
	LED_Init();  //LED初始化
    EXTIX_Init();	//外部中断初始化	
	OSInit();  //UCOS初始化
	OSTaskCreate(start_task, (void*)0, (OS_STK*)&START_TASK_STK[START_STK_SIZE-1], START_TASK_PRIO); //创建开始任务
	OSStart(); //开始任务
}

/******************************************************
//创建UCOS开始任务
//提供外设初始化、创建信号量、创建邮箱、创建消息队列
、创建信号量集、创建任务、初始化统计任务等等
********************************************************/
void start_task(void *pdata)
{
    OS_CPU_SR cpu_sr = 0;
    pdata=pdata;
    u8 err; //错误标志码
    sem_TaskC = OSSemCreate(0); //创建信号量
    msg_TaskC = OSMboxCreate((void*) 0);//创建消息邮箱
    sem_Mutex = OSMutexCreate(8, &err);    //创建互斥信号量 设置优先级为8
    q_Msg = OSQCreate(&MsgGrp[0],256);  //创建消息队列
	OSStatInit();  //开启统计任务	
	OS_ENTER_CRITICAL();  //进入临界区(关闭中断)
    OSTaskCreate(ledA_task,(void*)0,(OS_STK*)&LEDA_TASK_STK[LEDA_STK_SIZE-1],LEDA_TASK_PRIO);//创建任务A
    OSTaskCreate(ledB_task,(void*)0,(OS_STK*)&LEDB_TASK_STK[LEDB_STK_SIZE-1],LEDB_TASK_PRIO);//创建任务B
    OSTaskCreate(ledC_task,(void*)0,(OS_STK*)&LEDC_TASK_STK[LEDC_STK_SIZE-1],LEDC_TASK_PRIO);//创建任务C
    OSTaskCreate(ledD_task,(void*)0,(OS_STK*)&LEDD_TASK_STK[LEDD_STK_SIZE-1],LEDD_TASK_PRIO);//创建任务D
    OSTaskCreate(ledE_task,(void*)0,(OS_STK*)&LEDE_TASK_STK[LEDE_STK_SIZE-1],LEDE_TASK_PRIO);//创建任务E
    OSTaskCreate(ledF_task,(void*)0,(OS_STK*)&LEDF_TASK_STK[LEDF_STK_SIZE-1],LEDF_TASK_PRIO);//创建任务F
    OSTaskCreate(keyG_task,(void*)0,(OS_STK*)&KEYG_TASK_STK[KEYG_STK_SIZE-1],KEYG_TASK_PRIO);//创建任务G
    OSTaskSuspend(START_TASK_PRIO);//挂起开始任务  
    OS_EXIT_CRITICAL();  //退出临界区(开中断)
}
 
/********************任务A任务函数****************************
1. 任务A小灯以1Hz的频率闪烁
2. 当任务A的小灯闪烁10次过后,任务A挂起,任务B执行
*************************************************************/
void ledA_task(void *pdata)
{
    pdata=pdata;
    NVIC_InitTypeDef   NVIC_InitStructure;
	while(1)
	{      
        if(21 <= taskA_Num)
        {
             LED0 = 1;
             NVIC_InitStructure.NVIC_IRQChannel=TIM3_IRQn; //定时器3中断
             NVIC_InitStructure.NVIC_IRQChannelCmd=DISABLE;
	         NVIC_Init(&NVIC_InitStructure);
             OSTaskSuspend(LEDA_TASK_PRIO);//当小灯闪烁10次后,挂起任务A         
        } 
	}
}

/********************任务B任务函数****************************
1. 任务B小灯以10Hz的频率闪烁
2. 当任务B的小灯闪烁20次过后,任务B挂起,给任务C发信号量
*************************************************************/
void ledB_task(void *pdata)
{
    pdata=pdata;
    u8 taskB_Num = 0 ;//记录任务A小灯闪烁次数
	while(1)
	{
		LED1 = 0;
		delay_ms(50);
		LED1 = 1;
		delay_ms(50);
        taskB_Num++;     
        if(20 <= taskB_Num)
        {
          OSSemPost(sem_TaskC);//给任务C发信号量
          OSTaskSuspend(LEDB_TASK_PRIO);//当小灯闪烁20次后,挂起任务B        
        }
	}
}

/********************任务C任务函数****************************
1. 任务C接收到任务B的信号量时,任务C执行
2. 对于外部中断的触发次数进行计数,并通过邮箱发送给任务C
3. 中断次数小于5时,任务C小灯以1Hz频率闪烁,
   中断次数大于等于5,且小于10时,任务C小灯以10Hz频率闪烁
4. 任务C把通过邮箱接收到的数据填入队列
*************************************************************/
void ledC_task(void *pdata)
{
    pdata=pdata;
    u8 err; 
    while (1)
    {
        OSSemPend(sem_TaskC, 0, &err);  //请求信号量
        I3_Num = (u16* )OSMboxPend(msg_TaskC, 10, &err);   //请求邮箱
        if (I3_Num != NULL) //如果消息邮箱没有消息,返回空指针。
        {
            I3_InNum = *I3_Num;
            OSQPost(q_Msg, I3_Num); //发送队列消息
         }
        if (I3_InNum < 5)
        {
            LED2 = 0;
            delay_ms(500);
	        LED2 = 1;
	        delay_ms(500);  
        }
        else if (I3_InNum >= 5 && I3_InNum < 10)
        {
            LED2 = 0;
            delay_ms(50);
	        LED2 = 1;
	        delay_ms(50);
        }
        OSSemPost(sem_TaskC);
  }
}

/********************任务D任务函数****************************
1、任务D获取到互斥量时任务D小灯常亮,未获取到时任务D小灯长灭
2、当外部输入1为高电平时,任务D尝试获取互斥量,
   当外部输入1为低电平时,任务D释放互斥量
*************************************************************/
void ledD_task(void *pdata)
{
    pdata=pdata;
    u8 err;
	while(1)
	{
       if (KEY0 == 0)   //外部输入1(PI_15)为高电平,尝试获取互斥信号量
       {
           OSMutexPend(sem_Mutex, 10, &err);
           if (err != 10u)
           {
             LED3 = 0;
           }     
       }
       if (KEY0 == 1)   //外部输入1为低电平,释放互斥信号量
       {
           OSMutexPost(sem_Mutex);
           LED3 = 1;
           
       }
       delay_ms(10);   //任务调度   
	}
}

/********************任务E任务函数****************************
1、任务E获取到互斥量时任务E小灯常亮,未获取到时任务E小灯长灭
2、当外部输入2为高电平时,任务E尝试获取互斥量,
   当外部输入2为低电平时,任务E释放互斥量
*************************************************************/
void ledE_task(void *pdata)
{
    pdata=pdata;
    u8 err;
	while(1)
	{                       
        if (KEY1 == 0)   //外部输入2为高电平,尝试获取互斥信号量
        {
            OSMutexPend(sem_Mutex, 10, &err);    
            if (err != 10u)
            {
                 LED4 = 0;
            }
        }
        if (KEY1 == 1)   //外部输入2为低电平,释放互斥信号量
        {
            OSMutexPost(sem_Mutex);
            LED4 = 1;
        }
        delay_ms(10);     
	}
}

/********************任务F任务函数****************************
1、任务E获取到互斥量时任务E小灯常亮,未获取到时任务E小灯长灭
2、当外部输入2为高电平时,任务E尝试获取互斥量,
   当外部输入2为低电平时,任务E释放互斥量
*************************************************************/
void ledF_task(void *pdata)
{
    pdata=pdata;
    u8 err;
    u8 num;
    u8 *p;
	while(1)
	{
        p = OSQPend(q_Msg, 0, &err);  //请求消息队列
        if (p != NULL)
        {
            num = *p % 8;
        }
        switch (num)
        {
            case 0:
                LED5 = 1; LED6 = 1; LED7 = 1; LED8 = 1; LED9 = 1; LED10 = 1; LED11 = 1; 
                LED12 = 1; LED13 = 1; LED14 = 1; LED15 = 1;
                break;
            case 1:
                LED5 = 0; LED6 = 1; LED7 = 1; LED8 = 1; LED9 = 1; LED10 = 1; LED11 = 1; 
                LED12 = 1; LED13 = 1; LED14 = 1; LED15 = 1;
                break;
            case 2:
                LED5 = 0; LED6 = 0; LED7 = 1; LED8 = 1; LED9 = 1; LED10 = 1; LED11 = 1; 
                LED12 = 1; LED13 = 1; LED14 = 1; LED15 = 1;
                break;
            case 3:
                LED5 = 0; LED6 = 0; LED7 = 0; LED8 = 1; LED9 = 1; LED10 = 1; LED11 = 1; 
                LED12 = 1; LED13 = 1; LED14 = 1; LED15 = 1;
                break;
            case 4:
                LED5 = 0; LED6 = 0; LED7 = 0; LED8 = 0; LED9 = 1; LED10 = 1; LED11 = 1; 
                LED12 = 1; LED13 = 1; LED14 = 1; LED15 = 1;
                break;
            case 5:
                LED5 = 0; LED6 = 0; LED7 = 0; LED8 = 0; LED9 = 0; LED10 = 1; LED11 = 1; 
                LED12 = 1; LED13 = 1; LED14 = 1; LED15 = 1;
                break;
            case 6:
                LED5 = 0; LED6 = 0; LED7 = 0; LED8 = 0; LED9 = 0; LED10 = 0; LED11 = 1; 
                LED12 = 1; LED13 = 1; LED14 = 1; LED15 = 1;
                break;
            case 7:
                LED5 = 0; LED6 = 0; LED7 = 0; LED8 = 0; LED9 = 0; LED10 = 0; LED11 = 0; 
                LED12 = 1; LED13 = 1; LED14 = 1; LED15 = 1;
                break;
            case 8:
                LED5 = 0; LED6 = 0; LED7 = 0; LED8 = 0; LED9 = 0; LED10 = 0; LED11 = 0; 
                LED12 = 0; LED13 = 1; LED14 = 1; LED15 = 1;
                break;
            case 9:
                LED5 = 0; LED6 = 0; LED7 = 0; LED8 = 0; LED9 = 0; LED10 = 0; LED11 = 0; 
                LED12 = 0; LED13 = 0; LED14 = 1; LED15 = 1;
                break;
            case 10:
                LED5 = 0; LED6 = 0; LED7 = 0; LED8 = 0; LED9 = 0; LED10 = 0; LED11 = 0; 
                LED12 = 0; LED13 = 0; LED14 = 0; LED15 = 1;
                break;
            case 11:
                LED5 = 0; LED6 = 0; LED7 = 0; LED8 = 0; LED9 = 0; LED10 = 0; LED11 = 0; 
                LED12 = 0; LED13 = 0; LED14 = 0; LED15 = 0;
                break;
            default:
                LED5 = 0; LED6 = 0; LED7 = 0; LED8 = 0; LED9 = 0; LED10 = 0; LED11 = 0; 
                LED12 = 0; LED13 = 0; LED14 = 0; LED15 = 0;
                break;
        }
        delay_ms(20);   //任务调度
	}
}



//按键检测任务
void keyG_task(void *pdata)
{
    pdata=pdata;
    NVIC_InitTypeDef   NVIC_InitStructure;
    while (1)
    {
        if (flag_Exit == 1)
        { 
            NVIC_InitStructure.NVIC_IRQChannel = EXTI4_IRQn;//外部中断4
            NVIC_InitStructure.NVIC_IRQChannelCmd = DISABLE;//使能外部中断通道
            NVIC_Init(&NVIC_InitStructure);//配置           
            PB4_Num++;
            OSMboxPost(msg_TaskC, &PB4_Num);  //发送邮箱消息
            delay_ms(500);    
            NVIC_InitStructure.NVIC_IRQChannel = EXTI4_IRQn;//外部中断4
            NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;//使能外部中断通道
            NVIC_Init(&NVIC_InitStructure);//配置  
            flag_Exit = 0;
        }
    }
    
}

//外部中断4服务程序---由PB4下降沿触发
//中断服务程序不能调用可能会导致任务调度的函数
void EXTI4_IRQHandler(void)
{
    
    OSIntEnter(); //进入中断处理
    flag_Exit = 1;
    EXTI_ClearITPendingBit(EXTI_Line4);//清除LINE4上的中断标志位  
     
    OSIntExit();   //退出中断处理
}


//定时器3中断服务函数
void TIM3_IRQHandler(void)
{
    OSIntEnter(); //进入中断处理
	if(TIM_GetITStatus(TIM3,TIM_IT_Update)==SET) //溢出中断
	{
		LED0=!LED0;//LED0翻转
        taskA_Num++;
	}
	TIM_ClearITPendingBit(TIM3,TIM_IT_Update);  //清除中断标志位
    OSIntExit();   //退出中断处理
}