梳理STM32F429之通信传输部分---NO.3 串口空闲IDEL中断+DMA+FreeRTOS

目录

一、串口空闲IDEL中断:

二、串口及串口中断的配置:

三、stm32f4xx_it.h 的配置:

四、DMA 的配置:

五、主函数


如果大家有什么问题,欢迎在下面评论交流!

串口部分的详解梳理STM32F429之通信传输部分---NO.1 串口通讯

DMA直接存储器访问梳理STM32F429之通信传输部分---NO.2 DMA—直接存储区访问

FreeRTOS-二值信号量从0到1学习FreeRTOS:FreeRTOS 内核应用开发:(十一)信号量 NO.1 基本概念

                                        从0到1学习FreeRTOS:FreeRTOS 内核应用开发:(十二)信号量 NO.2 常用信号量函数接口讲解

                                        从0到1学习FreeRTOS:FreeRTOS 内核应用开发:(十三)信号量 NO.3 信号量实验

这里补充一下:串口空闲IDEL中断的一些注意事项:

一、串口空闲IDEL中断:

1、串口空闲IDEL中断的作用:

       现在有很多数据处理都要用到不定长数据,而单片机串口的RXNE中断一次只能接收一个字节的数据,没有缓冲区,无法接收一帧多个数据,利用串口IDLE空闲中断的方式接收一帧数据,方法如下:

       实现思路:采用STM32F429的串口1,并配置成空闲中断IDLE模式且使能DMA接收,并同时设置接收缓冲区和初始化DMA。那么初始化完成之后,当外部给单片机发送数据的时候,假设这帧数据长度是200个字节,那么在单片机接收到一个字节的时候并不会产生串口中断,而是DMA在后台把数据默默地搬运到你指定的缓冲区里面。当整帧数据发送完毕之后串口才会产生一次中断,此时可以利用DMA_GetCurrDataCounter();函数计算出本次的数据接受长度,从而进行数据处理

2、注意事项:避免一直进入空闲中断

       空闲中断是在检测到在数据收受后,总线上在一个字节的时间内没有再接收到数据时发生。即串口的RXNE位被置位之后才开始检测,检测到空闲之后,串口的CR1寄存器的IDLE位被硬件置1,必须采用软件将IDLE位清零才能避免反复进入空闲中断。具体的做法是先读取USART_SR,再读取USART_DR。需要注意的是,不能采用库函数USART_ClearFlag()或者USART_ClearItPending()来清除IDEL标志位。

USART_ReceiveData(USART1);    清除标志位 

二、串口及串口中断的配置:

bsp_usart.c

/**
  ******************************************************************************
  * @file    bsp_usart.c
  * @author  Sumjess
  * @version V1.0
  * @date    2019-09-xx
  * @brief   MDK5.27
  ******************************************************************************
  * @attention
  *
  * 实验平台   :STM32 F429 
  * CSDN Blog  :https://blog.csdn.net/qq_38351824
  * 微信公众号 :Tech云
  *
  ******************************************************************************
  */
 
#include "bsp_usart.h"
 
 /**
  * @brief  配置嵌套向量中断控制器NVIC
  * @param  无
  * @retval 无
  */
 
// 中断服务函数  在#include "stm32f4xx_it.h"中  为了防止写错中断服务函数,请去startup_stm32f429_439xx.s中粘贴
static void NVIC_Configuration(void)
{
  NVIC_InitTypeDef NVIC_InitStructure;
  
  /* 嵌套向量中断控制器组选择 */
  NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);
  
  /* 配置USART为中断源 */
  NVIC_InitStructure.NVIC_IRQChannel = DEBUG_USART_IRQ;     //USART1_IRQn  串口1
  /* 抢断优先级为1 */
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 8;
  /* 子优先级为1 */
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
  /* 使能中断 */
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  /* 初始化配置NVIC */
  NVIC_Init(&NVIC_InitStructure);
}
 
 
void Debug_USART_Config(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;
  USART_InitTypeDef USART_InitStructure;
/
//--------------------------------------串口1--------------------------------------------------//
 
/* -----------------------------串口相关的GPIO初始化-------------------------------------- */	
  RCC_AHB1PeriphClockCmd(DEBUG_USART1_RX_GPIO_CLK|DEBUG_USART1_TX_GPIO_CLK,ENABLE);
  /* 使能 USART 时钟 */
  RCC_APB2PeriphClockCmd(DEBUG_USART1_CLK, ENABLE);  //注意!只有串口1和6是APB2为90M,其他均为APB1为45M
  
  /* GPIO初始化 */
  GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;     //输出推挽
  GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;       //上拉
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;  //速度50MHZ
  
  /* 配置Tx引脚为复用功能  */
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;       //复用功能模式
  GPIO_InitStructure.GPIO_Pin = DEBUG_USART1_TX_PIN  ;//GPIO_Pin_9  
  GPIO_Init(DEBUG_USART1_TX_GPIO_PORT, &GPIO_InitStructure); //GPIOA
 
  /* 配置Rx引脚为复用功能 */
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;       //复用功能模式
  GPIO_InitStructure.GPIO_Pin = DEBUG_USART1_RX_PIN;  //GPIO_Pin_10 
  GPIO_Init(DEBUG_USART1_RX_GPIO_PORT, &GPIO_InitStructure);
	
	 /* 连接 PXx 到 USARTx_Tx*/
  GPIO_PinAFConfig(DEBUG_USART1_RX_GPIO_PORT,DEBUG_USART1_RX_SOURCE,DEBUG_USART1_RX_AF);//GPIOA---9---GPIO_AF_USART1
  /*  连接 PXx 到 USARTx__Rx*/
  GPIO_PinAFConfig(DEBUG_USART1_TX_GPIO_PORT,DEBUG_USART1_TX_SOURCE,DEBUG_USART1_TX_AF);//GPIOA--10---GPIO_AF_USART1
	
	/* -----------------------------串口配置-------------------------------------- */	
	
	/* 配置串DEBUG_USART 模式 */
  /* 波特率设置:DEBUG_USART_BAUDRATE */
  USART_InitStructure.USART_BaudRate = DEBUG_USART1_BAUDRATE;     //115200 串口波特率
  /* 字长(数据位+校验位):8 */
  USART_InitStructure.USART_WordLength = USART_WordLength_8b;    
  /* 停止位:1个停止位 */
  USART_InitStructure.USART_StopBits = USART_StopBits_1;         
  /* 校验位选择:不使用校验 */
  USART_InitStructure.USART_Parity = USART_Parity_No;            
  /* 硬件流控制:不使用硬件流 */                                
  USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
  /* USART模式控制:同时使能接收和发送 */
  USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
  /* 完成USART初始化配置 */
  USART_Init(DEBUG_USART1, &USART_InitStructure);
 
	/* 配置串口的接收中断 */
	NVIC_Configuration();
	
/	
//USART_IT_IDLE和USART_IT_RXNE区别 
 
//当接收到1个字节,会产生USART_IT_RXNE中断 
 
//当接收到一帧数据,就会产生USART_IT_IDLE中断
 
	// 开启 串口空闲IDEL 中断
	USART_ITConfig(DEBUG_USART1, USART_IT_IDLE, ENABLE);	
//	/* 使能串口接收中断 */
//	USART_ITConfig(DEBUG_USART1, USART_IT_RXNE, ENABLE);
/
 
 
	  /* 使能串口 */
  USART_Cmd(DEBUG_USART1, ENABLE);
}
 
/*****************  发送一个字符 **********************/
void Usart_SendByte( USART_TypeDef * pUSARTx, uint8_t ch)
{
	/* 发送一个字节数据到USART */
	USART_SendData(pUSARTx,ch);
		
	/* 等待发送数据寄存器为空 */
	while (USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET);	
}
 
/*****************  发送一个16位数 **********************/
void Usart_SendHalfWord( USART_TypeDef * pUSARTx, uint16_t ch)
{
	uint8_t temp_h, temp_l;
	
	/* 取出高八位 */
	temp_h = (ch&0XFF00)>>8;
	/* 取出低八位 */
	temp_l = ch&0XFF;
	
	/* 发送高八位 */
	USART_SendData(pUSARTx,temp_h);	
	while (USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET);
	
	/* 发送低八位 */
	USART_SendData(pUSARTx,temp_l);	
	while (USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET);	
}
 
/*****************  发送字符串 **********************/
void Usart_SendString( USART_TypeDef * pUSARTx, char *str)
{
	unsigned int k=0;
  do 
  {
      Usart_SendByte( pUSARTx, *(str + k) );
      k++;
  } while(*(str + k)!='\0');
  
  /* 等待发送完成 */
  while(USART_GetFlagStatus(pUSARTx,USART_FLAG_TC)==RESET)
  {}
}
 
//重定向c库函数printf到串口,重定向后可使用printf函数
int fputc(int ch, FILE *f)
{
		/* 发送一个字节数据到串口 */
		USART_SendData(DEBUG_USART_printf_choose, (uint8_t) ch);
		
		/* 等待发送完毕 */
		while (USART_GetFlagStatus(DEBUG_USART_printf_choose, USART_FLAG_TXE) == RESET);		
	
		return (ch);
}
 
//重定向c库函数scanf到串口,重写向后可使用scanf、getchar等函数
int fgetc(FILE *f)
{
		/* 等待串口输入数据 */
		while (USART_GetFlagStatus(DEBUG_USART_printf_choose, USART_FLAG_RXNE) == RESET);
 
		return (int)USART_ReceiveData(DEBUG_USART_printf_choose);
}

bsp_usart.h

#ifndef __BSP_USART_H
#define	__BSP_USART_H
/**
  ******************************************************************************
  * @file    bsp.usart.c
  * @author  Sumjess
  * @version V1.0
  * @date    2019-09-10
  * @brief   MDK5.27
  ******************************************************************************
  * @attention
  *
  * 实验平台   :STM32 F429 
  * CSDN Blog  :https://blog.csdn.net/qq_38351824
  * 微信公众号 :Tech云
  *
  ******************************************************************************
  */
#include "stm32f4xx.h"
#include <stdio.h>
//---------------------------------------------------------------------------------------------//
//
//① 重定向c库函数printf到串口,重定向后可使用printf等函数
//② 重定向c库函数scanf到串口,重写向后可使用scanf、getchar等函数
//选择使用串口被允许使用 ① ② 
#define DEBUG_USART_printf_choose   DEBUG_USART1
//
 
 
//------------------------------------------------------//
//引脚定义
/*******************************************************/
 
 
#define DEBUG_USART1                             USART1
#define DEBUG_USART1_CLK                         RCC_APB2Periph_USART1  //注意!只有串口1和6是APB2为90M,其他均为APB1为45M
#define DEBUG_USART1_BAUDRATE                    115200  //串口波特率
 
#define DEBUG_USART1_RX_GPIO_PORT                GPIOA
#define DEBUG_USART1_RX_GPIO_CLK                 RCC_AHB1Periph_GPIOA
#define DEBUG_USART1_RX_PIN                      GPIO_Pin_10
#define DEBUG_USART1_RX_AF                       GPIO_AF_USART1  //映射
#define DEBUG_USART1_RX_SOURCE                   GPIO_PinSource10
//!!!一个引脚有许多功能,通过映射将引脚与你要选的功能连接起来。
#define DEBUG_USART1_TX_GPIO_PORT                GPIOA
#define DEBUG_USART1_TX_GPIO_CLK                 RCC_AHB1Periph_GPIOA
#define DEBUG_USART1_TX_PIN                      GPIO_Pin_9
#define DEBUG_USART1_TX_AF                       GPIO_AF_USART1  //映射
#define DEBUG_USART1_TX_SOURCE                   GPIO_PinSource9
 
//中断部分配置//
#define DEBUG_USART_IRQHandler                  USART1_IRQHandler
#define DEBUG_USART_IRQ                 				USART1_IRQn     //串口1中断
 
 
void Debug_USART_Config(void);                                 //串口初始化
void Usart_SendByte( USART_TypeDef * pUSARTx, uint8_t ch);     //发送一个字符
void Usart_SendHalfWord( USART_TypeDef * pUSARTx, uint16_t ch);//发送一个16位数
void Usart_SendString( USART_TypeDef * pUSARTx, char *str);    //发送字符串
int fputc(int ch, FILE *f);                                    //重定向c库函数printf到串口,重定向后可使用printf函数
int fgetc(FILE *f);                                            //重定向c库函数scanf到串口,重写向后可使用scanf、getchar等函数

三、stm32f4xx_it.h 的配置:

/* 声明引用外部二值信号量 */
extern SemaphoreHandle_t BinarySem_Handle;
 
void USART1_IRQHandler(void)
{
  uint32_t ulReturn;
  /* 进入临界段,临界段可以嵌套 */
  ulReturn = taskENTER_CRITICAL_FROM_ISR();
	if(USART_GetITStatus(USART1,USART_IT_IDLE)!=RESET)
	{	
		Uart_DMA_Rx_Data();       /* 释放一个信号量,表示数据已接收 */
		USART_ReceiveData(USART1); /* 清除标志位 */
	}	 
  /* 退出临界段 */
  taskEXIT_CRITICAL_FROM_ISR( ulReturn );
}

四、DMA 的配置:

bsp_DMA.c

char RevBuff[REVBUFF_SIZE];
 
void USART1_RX_DMA_Config(void)
{
  DMA_InitTypeDef DMA_InitStructure;
 
  // 开启DMA时钟
  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2, ENABLE);
  // DMA复位
  DMA_DeInit(USART1_RX_DMA_STREAM);  
  // 设置DMA通道
  DMA_InitStructure.DMA_Channel = USART1_DMA_CHANNEL;  
  /*设置DMA源:串口数据寄存器地址*/
  DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)USART1_DR_BASE;
  // 内存地址(要传输的变量的指针)
  DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)RevBuff;
  // 方向:从内存到外设	
  DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory;
  // 传输大小	
  DMA_InitStructure.DMA_BufferSize = (uint32_t)REVBUFF_SIZE;
  // 外设地址不增	    
  DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
  // 内存地址自增
  DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
  // 外设数据单位	
  DMA_InitStructure.DMA_PeripheralDataSize = 
  DMA_PeripheralDataSize_Byte;
  // 内存数据单位
  DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;	 
  // DMA模式,一次或者循环模式
  //DMA_InitStructure.DMA_Mode = DMA_Mode_Normal ;
  DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;	
  // 优先级:中	
  DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh; 
  // 禁止内存到内存的传输
  /*禁用FIFO*/
  DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;        
  DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;    
  /*存储器突发传输 1个节拍*/
  DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;    
  /*外设突发传输 1个节拍*/
  DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;    
  /*配置DMA2的数据流7*/		   
  DMA_Init(USART1_RX_DMA_STREAM, &DMA_InitStructure);
  // 清除DMA所有标志
  DMA_ClearFlag(USART1_RX_DMA_STREAM,DMA_FLAG_TCIF2);
  DMA_ITConfig(USART1_RX_DMA_STREAM, DMA_IT_TE, ENABLE);
   // 开启串口DMA接收
	USART_DMACmd(USART1, USART_DMAReq_Rx, ENABLE); 
	// 使能DMA
  DMA_Cmd (USART1_RX_DMA_STREAM,ENABLE);
}
 
信号量
extern SemaphoreHandle_t BinarySem_Handle;
 
void Uart_DMA_Rx_Data(void)
{
  BaseType_t pxHigherPriorityTaskWoken;
  // 关闭DMA ,防止干扰
  DMA_Cmd(USART1_RX_DMA_STREAM, DISABLE);      
  // 清DMA标志位
  DMA_ClearFlag(USART1_RX_DMA_STREAM,DMA_FLAG_TCIF2);         
  //  重新赋值计数值,必须大于等于最大可能接收到的数据帧数目
  DMA_SetCurrDataCounter(USART1_RX_DMA_STREAM,REVBUFF_SIZE);     
  DMA_Cmd(USART1_RX_DMA_STREAM, ENABLE);       
  /* 
  xSemaphoreGiveFromISR(SemaphoreHandle_t xSemaphore,
                      BaseType_t *pxHigherPriorityTaskWoken);
  */
 
  //给出二值信号量 ,发送接收到新数据标志,供前台程序查询
  xSemaphoreGiveFromISR(BinarySem_Handle,&pxHigherPriorityTaskWoken);	//释放二值信号量
  //如果需要的话进行一次任务切换,系统会判断是否需要进行切换
  portYIELD_FROM_ISR(pxHigherPriorityTaskWoken);
 
}

bsp_DMA.h

/*存储器到外设串口1TX*/
#define USART1_DR_BASE               (uint32_t)(&USART1->DR)		//数据发送到 USART_DR 寄存器,该寄存器相对 USART 偏移地址为0x04
#define SENDBUFF_SIZE                5000				//发送的数据量  小于65535
 
#define USART1_DMA_CLK               RCC_AHB1Periph_DMA2	
#define USART1_DMA_CHANNEL           DMA_Channel_4       //通道4
#define USART1_TX_DMA_STREAM            DMA2_Stream7        //数据流 7
/*存储器到外设串口1RX*/
#define USART1_DR_BASE              (uint32_t)(&USART1->DR)		//数据发送到 USART_DR 寄存器,该寄存器相对 USART 偏移地址为0x04
#define REVBUFF_SIZE                 5000				//接收的数据量  小于65535
 
#define USART1_DMA_CLK               RCC_AHB1Periph_DMA2	
#define USART1_DMA_CHANNEL           DMA_Channel_4       //通道4
#define USART1_RX_DMA_STREAM         DMA2_Stream2        //数据流 2
 
void USART1_TX_DMA_Config(void);
void USART1_RX_DMA_Config(void);
void Uart_DMA_Rx_Data(void);

五、主函数

/**
  ******************************************************************************
  * @file    main.c
  * @author  Sumjess
  * @version V1.0
  * @date    2019-09-xx
  * @brief   MDK5.27
  ******************************************************************************
  * @attention
  *
  * 实验平台   :STM32 F429 
  * CSDN Blog  :https://blog.csdn.net/qq_38351824
  * 微信公众号 :Tech云
  *
  ******************************************************************************
  */
  
/*
*************************************************************************
*                             包含的头文件
*************************************************************************
*/
/* 标准库头文件 */
#include <string.h>
 
/* FreeRTOS头文件 */
#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"        //消息队列
#include "semphr.h"       //信号量、互斥信号量
#include "event_groups.h" //事件
#include "timers.h"       //软件定时器
 
/* 开发板硬件bsp头文件 */
#include "sum_common.h"                  
#include "limits.h"       //任务通知使用的
/**************************** 任务句柄 ********************************/
/* 
 * 任务句柄是一个指针,用于指向一个任务,当任务创建好之后,它就具有了一个任务句柄
 * 以后我们要想操作这个任务都需要通过这个任务句柄,如果是自身的任务操作自己,那么
 * 这个句柄可以为NULL。
 */
static TaskHandle_t AppTaskCreate_Handle = NULL;/* 创建任务句柄 */
 
static TaskHandle_t LED_Task_Handle = NULL;/* LED_Task任务句柄 */
static TaskHandle_t Uart_Task_Handle = NULL;/* KEY任务句柄 */
 
 
 
/********************************** 内核对象句柄 *********************************/
/*
 * 信号量,消息队列,事件标志组,软件定时器这些都属于内核的对象,要想使用这些内核
 * 对象,必须先创建,创建成功之后会返回一个相应的句柄。实际上就是一个指针,后续我
 * 们就可以通过这个句柄操作这些内核对象。
 *
 * 内核对象说白了就是一种全局的数据结构,通过这些数据结构我们可以实现任务间的通信,
 * 任务间的事件同步等各种功能。至于这些功能的实现我们是通过调用这些内核对象的函数
 * 来完成的
 * 
 */
 
SemaphoreHandle_t BinarySem_Handle =NULL;   //二值信号量
 
 
/******************************* 全局变量声明 ************************************/
/*
 * 当我们在写应用程序的时候,可能需要用到一些全局变量。
 */
extern char RevBuff[REVBUFF_SIZE];
 
 
 
/********************************** 宏定义 **************************************/
/*
 * 当我们在写应用程序的时候,可能需要用到一些宏定义。
 */
 
 
 
 
/*
*************************************************************************
*                             函数声明
*************************************************************************
*/
static void AppTaskCreate(void);/* 用于创建任务 */
 
static void LED_Task(void* pvParameters);/* LED_Task任务实现 */
static void Uart_Task(void* pvParameters);/* KEY_Task任务实现 */
 
static void BSP_Init(void);/* 用于初始化板载相关资源 */
 
 
/*****************************************************************
  * @brief  主函数
  * @param  无
  * @retval 无
  * @note   第一步:开发板硬件初始化 
            第二步:创建APP应用任务
            第三步:启动FreeRTOS,开始多任务调度
  ****************************************************************/
int main(void)
{	
  BaseType_t xReturn = pdPASS;/* 定义一个创建信息返回值,默认为pdPASS */
 
  /* 开发板硬件初始化 */
  BSP_Init();
	printf("开发板硬件初始化完毕!\r\n");
 
   /* 创建AppTaskCreate任务 */
  xReturn = xTaskCreate((TaskFunction_t )AppTaskCreate,  /* 任务入口函数---即任务函数的名称,需要我们自己定义并且实现。*/
                        (const char*    )"AppTaskCreate",/* 任务名字---字符串形式, 最大长度由 FreeRTOSConfig.h 中定义的configMAX_TASK_NAME_LEN 宏指定,多余部分会被自动截掉,这里任务名字最好要与任务函数入口名字一致,方便进行调试。*/
                        (uint16_t       )512,  /* 任务栈大小---字符串形式, 最大长度由 FreeRTOSConfig.h 中定义的configMAX_TASK_NAME_LEN 宏指定,多余部分会被自动截掉,这里任务名字最好要与任务函数入口名字一致,方便进行调试。*/
                        (void*          )NULL,/* 任务入口函数参数---字符串形式, 最大长度由 FreeRTOSConfig.h 中定义的configMAX_TASK_NAME_LEN 宏指定,多余部分会被自动截掉,这里任务名字最好要与任务函数入口名字一致,方便进行调试。*/
                        (UBaseType_t    )1, /* 任务的优先级---优先级范围根据 FreeRTOSConfig.h 中的宏configMAX_PRIORITIES 决定, 如果使能 configUSE_PORT_OPTIMISED_TASK_SELECTION,这个宏定义,则最多支持 32 个优先级;如果不用特殊方法查找下一个运行的任务,那么则不强制要求限制最大可用优先级数目。在 FreeRTOS 中, 数值越大优先级越高, 0 代表最低优先级。*/
                        (TaskHandle_t*  )&AppTaskCreate_Handle);/* 任务控制块指针---在使用内存的时候,需要给任务初始化函数xTaskCreateStatic()传递预先定义好的任务控制块的指针。在使用动态内存的时候,任务创建函数 xTaskCreate()会返回一个指针指向任务控制块,该任务控制块是 xTaskCreate()函数里面动态分配的一块内存。*/ 
  /* 启动任务调度 */           
  if(pdPASS == xReturn)
    vTaskStartScheduler();   /* 启动任务,开启调度 */
  else
    return -1;  
  
  while(1);   /* 正常不会执行到这里 */    
}
 
 
/***********************************************************************
  * @ 函数名  : AppTaskCreate
  * @ 功能说明: 为了方便管理,所有的任务创建函数都放在这个函数里面
  * @ 参数    : 无  
  * @ 返回值  : 无
  **********************************************************************/
static void AppTaskCreate(void)
{
  BaseType_t xReturn = pdPASS;/* 定义一个创建信息返回值,默认为pdPASS */
  
  taskENTER_CRITICAL();           //进入临界区
	
	
	
///  
  /* 创建LED_Task任务 */
  xReturn = xTaskCreate((TaskFunction_t )LED_Task, /* 任务入口函数 */
                        (const char*    )"LED_Task",/* 任务名字 */
                        (uint16_t       )512,   /* 任务栈大小 */
                        (void*          )NULL,	/* 任务入口函数参数 */
                        (UBaseType_t    )2,	    /* 任务的优先级 */
                        (TaskHandle_t*  )&LED_Task_Handle);/* 任务控制块指针 */
  if(pdPASS == xReturn)
    printf("创建LED_Task任务成功\r\n");  
///
  /* 创建 BinarySem */
  BinarySem_Handle = xSemaphoreCreateBinary();	 
  
	if(NULL != BinarySem_Handle)
    printf("BinarySem_Handle二值信号量创建成功!\r\n");
	
  /* 创建Uart_Task任务 */
  xReturn = xTaskCreate((TaskFunction_t )Uart_Task,  /* 任务入口函数 */
                        (const char*    )"Uart_Task",/* 任务名字 */
                        (uint16_t       )512,  /* 任务栈大小 */
                        (void*          )NULL,/* 任务入口函数参数 */
                        (UBaseType_t    )3, /* 任务的优先级 */
                        (TaskHandle_t*  )&Uart_Task_Handle);/* 任务控制块指针 */ 
  if(pdPASS == xReturn)
    printf("创建Uart_Task任务成功!\r\n");
///
  
  vTaskDelete(AppTaskCreate_Handle); //删除AppTaskCreate任务
  
  taskEXIT_CRITICAL();            //退出临界区
}
 
/**********************************************************************
  * @ 函数名  : LED_Task
  * @ 功能说明: LED_Task任务主体
  * @ 参数    :   
  * @ 返回值  : 无
  ********************************************************************/
static void LED_Task(void* parameter)
{	
  while (1)
  {
		LED1_ON;
		vTaskDelay(500);   /* 延时500个tick */
		printf("LED_Task Running,LED1_ON\r\n");
		
		LED1_OFF;     
		vTaskDelay(500);   /* 延时500个tick */		 		
		printf("LED_Task Running,LED1_OFF\r\n");
  }
}
/**********************************************************************
  * @ 函数名  : LED_Task
  * @ 功能说明: LED_Task任务主体
  * @ 参数    :   
  * @ 返回值  : 无
  ********************************************************************/
static void Uart_Task(void* parameter)
{	
	BaseType_t xReturn = pdPASS;/* 定义一个创建信息返回值,默认为pdPASS */
  while (1)
  {
    //获取二值信号量 xSemaphore,没获取到则一直等待
		xReturn = xSemaphoreTake(BinarySem_Handle,/* 二值信号量句柄 */
                              portMAX_DELAY); /* 等待时间 */
    if(pdPASS == xReturn)
    {
      LED2_TOGGLE;
      printf("收到数据:%s\r\n",RevBuff);
      memset(RevBuff,0,REVBUFF_SIZE);/* 清零 */
    }
  }                     
}
 
/***********************************************************************
  * @ 函数名  : BSP_Init
  * @ 功能说明: 板级外设初始化,所有板子上的初始化均可放在这个函数里面
  * @ 参数    :   
  * @ 返回值  : 无
  *********************************************************************/
static void BSP_Init(void)
{
	/*
	 * STM32中断优先级分组为4,即4bit都用来表示抢占优先级,范围为:0~15
	 * 优先级分组只需要分组一次即可,以后如果有其他的任务需要用到中断,
	 * 都统一用这个优先级分组,千万不要再分组,切忌。
	 */
	NVIC_PriorityGroupConfig( NVIC_PriorityGroup_4 );
	
	/* LED 初始化 */
	LED_GPIO_Config();
 
	/* 串口初始化	*/
	Debug_USART_Config();
  
	/* 按键初始化	*/
	Key_GPIO_Config();
	
	/* DMA初始化	*/
	USART1_RX_DMA_Config();
	
}
 
/********************************END OF FILE****************************/