queue. h
xQueueHandle xQueueCreate(
unsigned portBASE_TYPE uxQueueLength,
unsigned portBASE_TYPE uxItemSize
);
创建一个新的队列。为新的队列分配所需的存储内存,并返回一个队列处理。
uxQueueLength | 队列中包含最大项目数量。 |
---|---|
uxItemSize | 队列中每个项目所需的字节数。项目通过复制而不是引用排队,因为,所需的字节数,将复制给每个项目。队列中每个项目必须分配同样大小。 |
如果队列成功创建,并返回一个新建队列的处理。如果不能创建队列,将返回0。
struct AMessage {
portCHAR ucMessageID;
portCHAR ucData[ 20 ];
};
void vATask( void *pvParameters ){
xQueueHandle xQueue1, xQueue2;
// 创建一个队列,包含10个unsigned long值
xQueue1 = xQueueCreate( 10, sizeof( unsigned portLONG ) );
if( xQueue1 == 0 ){
// 队列不能创建,就不能使用
}
// 创建一个队列,包含10个指向AMessage 结构的指针
/// 可以通过指针传递,指针可以包含很多数据
xQueue2 = xQueueCreate( 10, sizeof( struct AMessage * ) );
if( xQueue2 == 0 ){
// 队列不能创建,就不能使用
}
// ... 其余代码
}
queue.h
portBASE_TYPE xQueueSend(
xQueueHandle xQueue,
const void * pvItemToQueue,
portTickType xTicksToWait
);
是一个调用xQueueGenericSend()的宏。能向后兼容FreeRTOS.org(没有包括xQueueSendToFront()和xQueueSendToBack() 宏的)版本。与xQueueSendToBack()等效。
传递一个项目到队列。这个项目通过复制而不是通过引用排队。这个函数不能从中断服务程序调用。参考xQueueSendFromISR(),在ISR中交错使用。
xQueueSend() 是全特点任务间通信API接口。xQueueSend() 等效于交叉API。版本不要同样的参数和同样的返回值。
Parameters:
xQueue 处理将项目传递给队列pvItemToQueue 指向队列中放置的项目的指针。项目的大小,由队列创建时定义,因为许多字节可以从 pvItemToQueue复制到队列的储存区域 xTicksToWait 最大时间量(任务应该锁住,等待队列中的可用空间)应该已经满了。如果设置为0,调用将立即返回。时间使用滴答周期来定义,因此如果需要,常量portTICK_RATE_MS应该用来转换实时时间
Returns:
pdTRUE:项目成功传递。否则为:errQUEUE_FULL.
queue.h
portBASE_TYPE xQueueSend(
xQueueHandle xQueue,
const void * pvItemToQueue,
portTickType xTicksToWait
);
一个调用 xQueueGenericReceive() 函数的宏。
从队列接收一个项目。这个项目通过复制接收,因此缓冲器必须提供足够大的空间。复制进缓冲器的字节数,在队列创建时已经定义。
这个函数一定不能在中断服务程序中使用。参考 xQueueReceiveFromISR 获得能够的选择。
xQueueReceive() 是全功能任务间通信API接口。xQueueAltReceive() 相当于API其中之一。版本需要相同的参数和相同的返回值。
pxQueue | 将要接收项目的队列句柄 |
---|---|
pvBuffer | 指向将要复制接收项目的缓冲器的指针。 |
xTicksToWait | 任务中断并等待队列中可用空间的最大时间,应该是满的。如果设置为0,调用将立刻返回。时间在片区间中定义,如果需要,portTICK_RATE_MS常量用来转换为实际时间。 如果 INCLUDE_vTaskSuspend 定义为1 ,指定的中断时间( portMAX_DELAY) 将导致任务无限期中断(没有时间溢出)。 |
如果项目成功被队列接收为pdTRUE 。 否则为 pdFALSE.
#include "sys.h"
#include "delay.h"
#include "usart.h"
#include "led.h"
#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
#include "key.h"
//任务优先级
#define START_TASK_PRIO 1
//任务堆栈大小
#define START_STK_SIZE 128
//任务句柄
TaskHandle_t StartTask_Handler;
//任务函数
void start_task(void *pvParameters);
//任务优先级
#define LED0_TASK_PRIO 3
//任务堆栈大小
#define LED0_STK_SIZE 50
//任务句柄
TaskHandle_t LED0Task_Handler;
//任务函数
void led0_task(void *pvParameters);
//任务优先级
#define LED1_TASK_PRIO 2
//任务堆栈大小
#define LED1_STK_SIZE 50
//任务句柄
TaskHandle_t LED1Task_Handler;
//任务函数
void led1_task(void *pvParameters);
//任务优先级
#define LED2_TASK_PRIO 1
//任务堆栈大小
#define LED2_STK_SIZE 50
//任务句柄
TaskHandle_t LED2Task_Handler;
//任务函数
void led2_task(void *pvParameters);
QueueHandle_t key_Queue;
int main(void)
{
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);//设置系统中断优先级分组4
delay_init(); //延时函数初始化
uart_init(115200); //初始化串口
LED_Init(); //初始化LED
KEY_Init();
//创建开始任务
xTaskCreate((TaskFunction_t )start_task, //任务函数
(const char* )"start_task", //任务名称
(uint16_t )START_STK_SIZE, //任务堆栈大小
(void* )NULL, //传递给任务函数的参数
(UBaseType_t )START_TASK_PRIO, //任务优先级
(TaskHandle_t* )&StartTask_Handler); //任务句柄
vTaskStartScheduler(); //开启任务调度
}
//开始任务任务函数
void start_task(void *pvParameters)
{
taskENTER_CRITICAL(); //进入临界区
key_Queue=xQueueCreate( 5, sizeof(u8));
if(key_Queue==NULL) printf("创建失败\r\n");//当内存空间不够时会失败
else
printf("创建成功\r\n");
//创建LED0任务
xTaskCreate((TaskFunction_t )led0_task,
(const char* )"led0_task",
(uint16_t )LED0_STK_SIZE,
(void* )NULL,
(UBaseType_t )LED0_TASK_PRIO,
(TaskHandle_t* )&LED0Task_Handler);
//创建LED1任务
xTaskCreate((TaskFunction_t )led1_task,
(const char* )"led1_task",
(uint16_t )LED1_STK_SIZE,
(void* )NULL,
(UBaseType_t )LED1_TASK_PRIO,
(TaskHandle_t* )&LED1Task_Handler);
//创建LED2任务
xTaskCreate((TaskFunction_t )led2_task,
(const char* )"led2_task",
(uint16_t )LED2_STK_SIZE,
(void* )NULL,
(UBaseType_t )LED2_TASK_PRIO,
(TaskHandle_t* )&LED2Task_Handler);
vTaskDelete(StartTask_Handler); //删除开始任务
taskEXIT_CRITICAL(); //退出临界区
}
//LED0任务函数
void led0_task(void *pvParameters)
{
u8 key=0;
BaseType_t r;
while(1) {
key = KEY_Scan(0);
if(key){
r = xQueueSend( key_Queue, &key, 10 ); //portMAX_DELAY 死等
if(r == pdPASS) printf("入队成功\n\r");
else printf("入队失败");
}
vTaskDelay(10);
}
}
//LED1任务函数
void led1_task(void *pvParameters)
{
u8 kk = 0;
BaseType_t rr;
while(1)
{
rr = xQueueReceive(key_Queue, &kk, portMAX_DELAY );
rr == pdPASS ? printf("出队成功\r\n") : printf("出队失败\r\n");
printf("出队数据是%d\r\n",kk);
vTaskDelay(10);
}
}
void led2_task(void *pvParameters)
{
while(1)
{
LED0=0;
vTaskDelay(1000);
LED0=1;
vTaskDelay(1000);
}
}
queue.h
portBASE_TYPE xQueueSendFromISR(
xQueueHandle pxQueue,
const void *pvItemToQueue,
portBASE_TYPE *pxHigherPriorityTaskWoken
);
这是一个调用 xQueueGenericSendFromISR() 的宏。是为了兼容 FreeRTOS.org 以后的版本(没有包含xQueueSendToBackFromISR() 和 xQueueSendToFrontFromISR() 宏)
传递一个项到队列的后面。在终端服务程序中可以安全使用。
项在队列中是复制而不是引用,排列小项目更加灵活,特别是当从ISR调用时。在大多数情况下,使用一个指向项目的指针传进队列更加灵活
xQueue | 将项目传进的队列 |
---|---|
pvItemToQueue | 一个指向将在队列中放置的项目的指针。项目的大小,队列在创建时已经定义了, 将从pvItemToQueue复制许多字节到队列的存储区域 |
pxHigherPriorityTaskWoken | 如果传进队列而导致任务解锁,并且解锁的任务的优先级高于当前运行任务的优先级xQueueSendFromISR将设置 *pxHigherPriorityTaskWoken到 pdTRUE 。如果xQueueSendFromISR()设置这个值到 pdTRUE,在中断推出之前将请求任务切换。 |
pdTRUE:数据成功传递进队列。否则为:errQUEUE_FULL。使用范例是缓冲IO(每次调用ISR能够更多的值)
Example usage for buffered IO (where the ISR can obtain more than one value per call):
void vBufferISR( void ){
portCHAR cIn;
portBASE_TYPE xHigherPriorityTaskWoken;
/* 没有在ISR中唤醒任务 */
xHigherPriorityTaskWoken = pdFALSE;
/* 循环直到缓冲器为空 */
do{
/*从缓冲器中获得一个字节. *
cIn = portINPUT_BYTE( RX_REGISTER_ADDRESS );
/* 传递字节 */
xQueueSendFromISR( xRxQueue, &cIn, &xHigherPriorityTaskWoken );
} while ( portINPUT_BYTE( BUFFER_COUNT ) );
/* 现在缓冲器为空,如果需要可以任务切换 */
if( xHigherPriorityTaskWoken ){
/* 实际宏使用了特殊接口 */
taskYIELD_FROM_ISR ();
}
}
queue. h
portBASE_TYPE xQueueReceiveFromISR(
xQueueHandle pxQueue,
void *pvBuffer,
portBASE_TYPE *pxTaskWoken
);
从队列接收一个项目。在中断程序中使用此函数是安全的。
Parameters:
pxQueue | 发送项目的队列句柄 |
---|---|
pvBuffer | 指向缓冲区的指针,将接收的项目被复制进去。 |
pxTaskWoken | 任务将锁住,等待队列中的可用空间。如果xQueueReceiveFromISR 引起一个任务解锁,pxTaskWoken 将设置为pdTRUE,否则pxTaskWoken保留不变 |
Returns: pdTRUE :如果项目成功从队列接收。否则为: pdFALSE
xQueueHandle xQueue;
// Function to create a queue and post some values.
void vAFunction( void *pvParameters ){
portCHAR cValueToPost;
const portTickType xBlockTime = ( portTickType )0xff;
// Create a queue capable of containing 10 characters.
xQueue = xQueueCreate( 10, sizeof( portCHAR ) );
if( xQueue == 0 ){
// Failed to create the queue.
}
// ...
// Post some characters that will be used within an ISR. If the queue
// is full then this task will block for xBlockTime ticks.
cValueToPost = 'a';
xQueueSend( xQueue, ( void * ) &cValueToPost, xBlockTime );
cValueToPost = 'b';
xQueueSend( xQueue, ( void * ) &cValueToPost, xBlockTime );
// ... keep posting characters ... this task may block when the queue
// becomes full.
cValueToPost = 'c';
xQueueSend( xQueue, ( void * ) &cValueToPost, xBlockTime );
}
// ISR that outputs all the characters received on the queue.
void vISR_Routine( void ){
portBASE_TYPE xTaskWokenByReceive = pdFALSE;
portCHAR cRxedChar;
while( xQueueReceiveFromISR( xQueue, ( void * ) &cRxedChar, &xTaskWokenByReceive) ){
// A character was received. Output the character now.
vOutputCharacter( cRxedChar );
// If removing the character from the queue woke the task that was
// posting onto the queue xTaskWokenByReceive will have been set to
// pdTRUE. No matter how many times this loop iterates only one
// task will be woken.
}
if( xTaskWokenByPost != pdFALSE ){
// We should switch context so the ISR returns to a different task.
// NOTE: How this is done depends on the port you are using. Check
// the documentation and examples for your port.
taskYIELD ();
}
}
// usart.c
#include "sys.h"
#include "usart.h"
#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
//
//加入以下代码,支持printf函数,而不需要选择use MicroLIB
#if 1
#pragma import(__use_no_semihosting)
//标准库需要的支持函数
struct __FILE
{
int handle;
};
FILE __stdout;
//定义_sys_exit()以避免使用半主机模式
void _sys_exit(int x)
{
x = x;
}
//重定义fputc函数
int fputc(int ch, FILE *f)
{
while((USART1->SR&0X40)==0);//循环发送,直到发送完毕
USART1->DR = (u8) ch;
return ch;
}
#endif
#if EN_USART1_RX //如果使能了接收
//串口1中断服务程序
//注意,读取USARTx->SR能避免莫名其妙的错误
u8 USART_RX_BUF[USART_REC_LEN]; //接收缓冲,最大USART_REC_LEN个字节.
//接收状态
//bit15, 接收完成标志
//bit14, 接收到0x0d
//bit13~0, 接收到的有效字节数目
u16 USART_RX_STA=0; //接收状态标记
void uart_init(u32 bound){
//GPIO端口设置
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1|RCC_APB2Periph_GPIOA, ENABLE); //使能USART1,GPIOA时钟
//USART1_TX GPIOA.9
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //PA.9
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出
GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.9
//USART1_RX GPIOA.10初始化
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;//PA10
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入
GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.10
//Usart1 NVIC 配置
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority= 6;//抢占优先级3
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; //子优先级3
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能
NVIC_Init(&NVIC_InitStructure); //根据指定的参数初始化VIC寄存器
//USART 初始化设置
USART_InitStructure.USART_BaudRate = bound;//串口波特率
USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字长为8位数据格式
USART_InitStructure.USART_StopBits = USART_StopBits_1;//一个停止位
USART_InitStructure.USART_Parity = USART_Parity_No;//无奇偶校验位
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//无硬件数据流控制
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; //收发模式
USART_Init(USART1, &USART_InitStructure); //初始化串口1
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//开启串口接受中断
USART_Cmd(USART1, ENABLE); //使能串口1
}
extern QueueHandle_t Key_Odd_Queue;//队列句柄
extern QueueHandle_t Key_Even_Queue;//队列句柄
void USART1_IRQHandler(void) //串口1中断服务程序
{
u8 len,i;
BaseType_t x_o = 0;
BaseType_t x_e = 0;
BaseType_t addr;
u8 Res;
char oddchars[20];
int o_i = 0;
char evenchars[20];
int e_i = 0;
if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)
{
Res =USART_ReceiveData(USART1); //读取接收到的数据
if((USART_RX_STA&0x8000)==0) //接收未完成 1000 0000 0000 0000
{
if(USART_RX_STA&0x4000) //接收到了0x0d 0100 0000 0000 0000
{
if(Res!=0x0a)USART_RX_STA=0; //接收错误,重新开始--接收到了0X0D但是没有接受到0x0A
else{
USART_RX_STA|=0x8000; //接收完成了--0x0d后面是0x0a
len=USART_RX_STA&0x3fff; // 0011 1111 1111 1111 获取长度
for(i=0;i<len;i++)
{
Res=USART_RX_BUF[i];
if(Res %2 == 0){
evenchars[o_i] = Res;
o_i++;
}
if(Res %2 == 1){
oddchars[e_i] = Res;
e_i++;
}
}
oddchars[o_i] = '\0';
evenchars[e_i] = '\0';
x_o = xQueueSendFromISR(Key_Odd_Queue,oddchars,&addr);
x_e = xQueueSendFromISR(Key_Even_Queue,evenchars,&addr);
USART_RX_STA=0;
// portMAX_DELAY 死等 0不等
if(x_o==pdPASS && x_e==pdPASS) printf("入队成功\r\n");
else printf("入队失败\r\n");
}
}
else
{
if(Res==0x0d)USART_RX_STA|=0x4000;
else
{
USART_RX_BUF[USART_RX_STA&0X3FFF]=Res ;
USART_RX_STA++;
if(USART_RX_STA>(USART_REC_LEN-1))USART_RX_STA=0;//接收数据错误,重新开始接收
}
}
}
}
}
#endif
// main.c
//LED0任务函数
void led0_task(void *pvParameters)
{
printf("LED0任务函数开始运行!\r\n");
char oddchar[20];
char evenchar[20];
char deschar[40];
u8 len_o = 0;
u8 len_e = 0;
BaseType_t rs_o;
BaseType_t rs_e;
int i = 0;
while(1){
rs_o=xQueueReceive(Key_Odd_Queue,oddchar,0); //奇数出队后删除
rs_e=xQueueReceive(Key_Even_Queue,evenchar,0); //偶数出队后删除
if(rs_o == pdTRUE && rs_e == pdTRUE){
printf("\r\n字符串出队%s%s",oddchar,evenchar);
}
vTaskDelay(20);
}
}