//此时heap_buf就相当于一块空闲的内存
//我们只需要在它上面实现内存的分配和释放,那么它就是一个堆
char heap_buf[1024];
int pos = 0; //指针(指向空闲内存的位置)
//分配空间
void * my_malloc(int size)
{
int old_pos = pos; //旧的下标
pos += size; //下标更新
return &heap_buf[old_pos]; //分配空间(返回首地址)
}
//释放分配出去的某一块内存(因为此时是没法释放的,所以就没写代码,意识一下即可)
void my_free(void * size)
{
/* err */
}
int main(void)
{
int i;
char * buf = my_malloc(100); //分配100个字节的空间
unsigned char uch = 200;
for(i=0;i<26;i++)
{
buf[i] = 'A' + i;
}
}
此时heap_buf就有数据了,并且下一次分配空间的时候,是从地址100开始分配的,因为pos指向100
void c_fun(void)
{
;
}
void b_fun(void)
{
;
}
int a_fun(int val)
{
int a = 8;
a += val;
b_fun();
c_fun();
return a;
}
int main(void)
{
a_fun(40);
return 0;
}
基本知识:
- 返回地址:可以理解为返回地址就是该函数执行结束后的下一条指令
在main函数中,a函数执行完之后将会返回到return 0中
a函数中调用函数b,当b函数执行之后将会返回到c函数调用前,c函数执行完之后返回到return a中
在c语言中,上面过程我们一目了然就知道了它返回的地址是谁,但这个返回地址保存在哪里呢?
1、返回地址保存在哪?
- 返回地址保存在栈中
- main在调用a_fun前会做两两件事情:
- 将a的返回地址(return 0的地址)保存到一个寄存器里面LR(link Register)
- 调用a_fun
- 那么a_fun里面调用b_fun之前,就要先把b_fun的返回地址(c_fun)保存到LR里面,之后在调用b_fun
2.那么在a_fun里面保存的LR会不会覆盖之前LR的数据呢?如果不会,LR里面是如何处理的?
- LR并不会被覆盖
- 在a_fun内部会做一件事情:
- 把LR的值(main里面的return 0的地址)存入栈中
- 同样道理,b_fun的内部也会将LR的值(c_fun的地址)存入栈中,b_fun执行完就会开始执行c_fun
- 当c_fun执行完,就会取出c_fun保存的LR值,并跳过去执行(return a),a函数也一样,最终跳到main函数的return 0
C函数开头:
- 划分栈(LR等寄存器、局部变量)
- 将LR等寄存器存入栈
- 执行代码
- 如代码里面有a = 8的话,会先把a在栈里面划分空间,之后再把8这个值写到栈中
下载
删减目录
编译、执行
添加串口打印功能
#include "FreeRTOS.h"
#include "queue.h"
#include "semphr.h"
#include
/* Library includes. */
#include "stm32f10x_lib.h"
/* Demo application includes. */
#include "serial.h"
/*-----------------------------------------------------------*/
/*-----------------------------------------------------------*/
/*
* 串口初始化函数
*/
void SerialPortInit(void)
{
USART_InitTypeDef USART_InitStructure;
GPIO_InitTypeDef GPIO_InitStructure;
/* Enable USART1 clock */
RCC_APB2PeriphClockCmd( RCC_APB2Periph_USART1 | RCC_APB2Periph_GPIOA, ENABLE );
/* Configure USART1 Rx (PA10) as input floating */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init( GPIOA, &GPIO_InitStructure );
/* Configure USART1 Tx (PA9) as alternate function push-pull */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_Init( GPIOA, &GPIO_InitStructure );
USART_InitStructure.USART_BaudRate = 115200;
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
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_InitStructure.USART_Clock = USART_Clock_Disable;
USART_InitStructure.USART_CPOL = USART_CPOL_Low;
USART_InitStructure.USART_CPHA = USART_CPHA_2Edge;
USART_InitStructure.USART_LastBit = USART_LastBit_Disable;
USART_Init( USART1, &USART_InitStructure );
USART_Cmd( USART1, ENABLE );
}
/*
* 实现fputc
*/
int fputc( int ch, FILE *f )
{
USART_TypeDef * USARTx = USART1;
//等待数据全部发送出去
while ( (USARTx->SR & (1<<7)) == 0); //如果状态寄存器的第7位(TXE)不等于1,则继续等待
USARTx->DR = ch; //往DR寄存器里写入数据
return ch;
}
int main( void )
{
#ifdef DEBUG
debug();
#endif
prvSetupHardware();
printf("Hello World\r\n");
/* Start the scheduler. */
vTaskStartScheduler();
/* Will only get here if there was not enough heap space to create the
idle task. */
return 0;
}
static void prvSetupHardware( void )
{
/* Start with the clocks in their expected state. */
RCC_DeInit();
/* Enable HSE (high speed external clock). */
RCC_HSEConfig( RCC_HSE_ON );
/* Wait till HSE is ready. */
while( RCC_GetFlagStatus( RCC_FLAG_HSERDY ) == RESET )
{
}
/* 2 wait states required on the flash. */
*( ( unsigned long * ) 0x40022000 ) = 0x02;
/* HCLK = SYSCLK */
RCC_HCLKConfig( RCC_SYSCLK_Div1 );
/* PCLK2 = HCLK */
RCC_PCLK2Config( RCC_HCLK_Div1 );
/* PCLK1 = HCLK/2 */
RCC_PCLK1Config( RCC_HCLK_Div2 );
/* PLLCLK = 8MHz * 9 = 72 MHz. */
RCC_PLLConfig( RCC_PLLSource_HSE_Div1, RCC_PLLMul_9 );
/* Enable PLL. */
RCC_PLLCmd( ENABLE );
/* Wait till PLL is ready. */
while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET)
{
}
/* Select PLL as system clock source. */
RCC_SYSCLKConfig( RCC_SYSCLKSource_PLLCLK );
/* Wait till PLL is used as system clock source. */
while( RCC_GetSYSCLKSource() != 0x08 )
{
}
/* Enable GPIOA, GPIOB, GPIOC, GPIOD, GPIOE and AFIO clocks */
RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB |RCC_APB2Periph_GPIOC
| RCC_APB2Periph_GPIOD | RCC_APB2Periph_GPIOE | RCC_APB2Periph_AFIO, ENABLE );
/* SPI2 Periph clock enable */
RCC_APB1PeriphClockCmd( RCC_APB1Periph_SPI2, ENABLE );
/* Set the Vector Table base address at 0x08000000 */
NVIC_SetVectorTable( NVIC_VectTab_FLASH, 0x0 );
NVIC_PriorityGroupConfig( NVIC_PriorityGroup_4 );
/* Configure HCLK clock as SysTick clock source. */
SysTick_CLKSourceConfig( SysTick_CLKSource_HCLK );
SerialPortInit(); //初始化串口1
}
void task1Function(void * Parameters)
{
while(1)
{
printf("1");
}
}
void task2Function(void * Parameters)
{
while(1)
{
printf("2");
}
}
/*-----------------------------------------------------------*/
int main( void )
{
//句柄(可以理解为一个任务的唯一标识)
TaskHandle_t xTaskHandle1; //创建任务1的句柄
#ifdef DEBUG
debug();
#endif
prvSetupHardware();
printf("Hello World\r\n");
/*
* pxTaskCode:函数
* pcName:任务名字
* usStackDepth:栈深度
* pvParameters:参数(给pxTaskCode函数用的)
* uxPriority:优先级
* pxCreatedTask:句柄
*/
//xTaskCreate(pxTaskCode,pcName,usStackDepth,pvParameters,uxPriority,pxCreatedTask);
//倘若任务1和任务2是在同一个串口输出的,那么就会交叉使用串口
//下面结果为:222222222222111111111112222222222221111111111122222222222211111111111
xTaskCreate(task1Function,"Task1",100,NULL,1,&xTaskHandle1); //创建任务1
xTaskCreate(task2Function,"Task2",100,NULL,1,NULL); //创建任务2
//FreeRtos是多任务系统,这个多任务在我们人类感觉上来说,是同时执行的,但其实是交叉执行的
//倘若任务1和任务2是不同的串口输出的,那么就会发现任务1和任务2“好像”是在同时执行的
//再比如添加一个LED闪烁任务,那么串口输出和LED闪烁“好像是同时执行的”
/* Start the scheduler. */
vTaskStartScheduler();
/* Will only get here if there was not enough heap space to create the
idle task. */
return 0;
}
以Keil工具下STM32F103芯片为例,它的FreeRTOS的目录如下:
主要涉及2个目录:
Demo
Demo目录下是工程文件,以"芯片和编译器"组合成一个名字
比如:CORTEX_STM32F103_Keil
Source
FreeRTOS的最核心文件只有2个:
其他文件的作用也一起列表如下:
FreeRTOS/Source/下的文件 | 作用 |
---|---|
tasks.c | 必需,任务操作 |
list.c | 必须,列表 |
queue.c | 基本必需,提供队列操作、信号量(semaphore)操作 |
timer.c | 可选,software timer |
event_groups.c | 可选,提供event group功能 |
croutine.c | 可选,过时了 |
每个移植的版本都含有自己的 portmacro.h 头文件,里面定义了2个数据类型:
变量名有前缀:
变量名前缀 | 含义 |
---|---|
c | char |
s | int16_t,short |
l | int32_t,long |
x | BaseType_t,其他非标准的类型:结构体、task handle、queue handle等 |
u | unsigned |
p | 指针 |
uc | uint8_t,unsigned char |
pc | char指针 |
函数名的前缀有2部分:返回值类型、在哪个文件定义
函数名前缀 | 含义 |
---|---|
vTaskPrioritySet | 返回值类型:void 在task.c中定义 |
xQueueReceive | 返回值类型:BaseType_t 在queue.c中定义 |
pvTimerGetTimerID | 返回值类型:pointer to void 在tmer.c中定义 |
宏的名字是大小,可以添加小写的前缀。前缀是用来表示:宏在哪个文件中定义。
宏的前缀 | 含义:在哪个文件里定义 |
---|---|
port (比如portMAX_DELAY) | portable.h或portmacro.h |
task (比如taskENTER_CRITICAL()) | task.h |
pd (比如pdTRUE) | projdefs.h |
config (比如configUSE_PREEMPTION) | FreeRTOSConfig.h |
err (比如errQUEUE_FULL) | projdefs.h |
通用的宏定义如下:
宏 | 值 |
---|---|
pdTRUE | 1 |
pdFALSE | 0 |
pdPASS | 1 |
pdFAIL | 0 |
对于每个任务都会有一个TCB_t结构体,对于TCB_t结构体我们可以选择动态分配或者静态分配
使用xTaskCreate函数创建的任务和栈都是动态分配的
一个任务可以简单的理解为是一个函数,在xTaskCreate函数中,我们还指定了栈的大小,因为在函数中有各种局部变量,以及各种调用,所以每一个任务的栈应该都不一样的,否则它们的栈会互相冲突,并且这个栈我们还可以静态分配
void task1Function(void * Parameters)
{
while(1)
{
printf("1");
}
}
void task2Function(void * Parameters)
{
while(1)
{
printf("2");
}
}
void task3Function(void * Parameters)
{
while(1)
{
printf("3");
}
}
/*-----------------------------------------------------------*/
//创建一个栈空间
StackType_t xTask3Stack[100]; //这里100的话深度(usStackDepth)也得写100
//创建TCB
StaticTask_t xTask3TCB;
//创建一个空闲任务
StackType_t xIdleTaskStack[100];
StaticTask_t xIdleTaskTCB;
//想要调用xTaskCreateStatic的话得要实现vApplicationGetIdleTaskMemory函数
void vApplicationGetIdleTaskMemory( StaticTask_t ** ppxIdleTaskTCBBuffer,
StackType_t ** ppxIdleTaskStackBuffer,
uint32_t * pulIdleTaskStackSize )
{
*ppxIdleTaskTCBBuffer = &xIdleTaskTCB;
*ppxIdleTaskStackBuffer = xIdleTaskStack;
*pulIdleTaskStackSize = 100;
}
int main( void )
{
//句柄(可以理解为一个任务的唯一标识)
TaskHandle_t xTaskHandle1; //创建句柄
#ifdef DEBUG
debug();
#endif
prvSetupHardware();
printf("Hello World\r\n");
//动态创建任务
xTaskCreate(task1Function,"Task1",100,NULL,1,&xTaskHandle1); //创建任务1
xTaskCreate(task2Function,"Task2",100,NULL,1,NULL); //创建任务2
/*
* 前面5个参数都跟xTaskCreate一样
* puxStackBuffer:传入一个栈,所谓栈就是一个空闲的内存,所以我们可以传一个数组
*/
//静态创建任务
xTaskCreateStatic(task3Function,"Task3",100,NULL,1,xTask3Stack,&xTask3TCB);
/* Start the scheduler. */
vTaskStartScheduler();
/* Will only get here if there was not enough heap space to create the
idle task. */
return 0;
}
高优先级的任务先执行,同优先级的任务交替执行
在下面逻辑分析仪中可以看出同优先级的任务他们的交叉执行
//用来标记任务是否运行
static int task1flagrun = 0;
static int task2flagrun = 0;
static int task3flagrun = 0;
/*-----------------------------------------------------------*/
void task1Function(void * Parameters)
{
while(1)
{
task1flagrun = 1;
task2flagrun = 0;
task3flagrun = 0;
printf("1");
}
}
void task2Function(void * Parameters)
{
while(1)
{
task1flagrun = 0;
task2flagrun = 1;
task3flagrun = 0;
printf("2");
}
}
void task3Function(void * Parameters)
{
while(1)
{
task1flagrun = 0;
task2flagrun = 0;
task3flagrun = 1;
printf("3");
}
}
/*-----------------------------------------------------------*/
//创建一个栈空间
StackType_t xTask3Stack[100]; //这里100的话深度(usStackDepth)也得写100
//创建TCB
StaticTask_t xTask3TCB;
//创建一个空闲任务
StackType_t xIdleTaskStack[100];
StaticTask_t xIdleTaskTCB;
//想要调用xTaskCreateStatic的话得要实现vApplicationGetIdleTaskMemory函数
void vApplicationGetIdleTaskMemory( StaticTask_t ** ppxIdleTaskTCBBuffer,
StackType_t ** ppxIdleTaskStackBuffer,
uint32_t * pulIdleTaskStackSize )
{
*ppxIdleTaskTCBBuffer = &xIdleTaskTCB;
*ppxIdleTaskStackBuffer = xIdleTaskStack;
*pulIdleTaskStackSize = 100;
}
int main( void )
{
//句柄(可以理解为一个任务的唯一标识)
TaskHandle_t xTaskHandle1; //创建句柄
#ifdef DEBUG
debug();
#endif
prvSetupHardware();
printf("Hello World\r\n");
//动态创建任务
xTaskCreate(task1Function,"Task1",100,NULL,1,&xTaskHandle1); //创建任务1
xTaskCreate(task2Function,"Task2",100,NULL,1,NULL); //创建任务2
/*
* 前面5个参数都跟xTaskCreate一样
* puxStackBuffer:传入一个栈,所谓栈就是一个空闲的内存,所以我们可以传一个数组
*/
//静态创建任务
xTaskCreateStatic(task3Function,"Task3",100,NULL,1,xTask3Stack,&xTask3TCB);
/* Start the scheduler. */
vTaskStartScheduler();
/* Will only get here if there was not enough heap space to create the
idle task. */
return 0;
}
将上面代码中task3Function改为优先级2,其他两个任务的优先级均为1
可以看出只有taskFunction在运行
高优先级的任务先执行,如果高优先级的任务没有主动放弃执行的话,其他低优先级的任务将不能执行
我们创建一个任务,并且传入了一个xTaskHandle1,让我们可以引用这个任务
想要删除任务,即必须要通过xTaskHandlex
vTaskDelete
//句柄(可以理解为一个任务的唯一标识)
TaskHandle_t xTaskHandle1; //创建句柄
TaskHandle_t xTaskHandle3; //创建句柄
//用来标记任务是否运行
static int task1flagrun = 0;
static int task2flagrun = 0;
static int task3flagrun = 0;
/*-----------------------------------------------------------*/
void task1Function(void * Parameters)
{
while(1)
{
task1flagrun = 1;
task2flagrun = 0;
task3flagrun = 0;
printf("1");
}
}
void task2Function(void * Parameters)
{
int i;
while(1)
{
task1flagrun = 0;
task2flagrun = 1;
task3flagrun = 0;
printf("2");
if(i++ == 100)
{
vTaskDelete(xTaskHandle1); //将任务1删除
}
if(i == 200)
{
vTaskDelete(xTaskHandle3); //将任务3删除
}
if(i == 300)
{
vTaskDelete(NULL); //将任务2删除(自杀)
}
}
}
void task3Function(void * Parameters)
{
while(1)
{
task1flagrun = 0;
task2flagrun = 0;
task3flagrun = 1;
printf("3");
}
}
/*-----------------------------------------------------------*/
//创建一个栈空间
StackType_t xTask3Stack[100]; //这里100的话深度(usStackDepth)也得写100
//创建TCB
StaticTask_t xTask3TCB;
//创建一个空闲任务
StackType_t xIdleTaskStack[100];
StaticTask_t xIdleTaskTCB;
//想要调用xTaskCreateStatic的话得要实现vApplicationGetIdleTaskMemory函数
void vApplicationGetIdleTaskMemory( StaticTask_t ** ppxIdleTaskTCBBuffer,
StackType_t ** ppxIdleTaskStackBuffer,
uint32_t * pulIdleTaskStackSize )
{
*ppxIdleTaskTCBBuffer = &xIdleTaskTCB;
*ppxIdleTaskStackBuffer = xIdleTaskStack;
*pulIdleTaskStackSize = 100;
}
int main( void )
{
#ifdef DEBUG
debug();
#endif
prvSetupHardware();
printf("Hello World\r\n");
xTaskCreate(task1Function,"Task1",100,NULL,1,&xTaskHandle1); //创建任务1
xTaskCreate(task2Function,"Task2",100,NULL,1,NULL); //创建任务2
//xTaskCreateStatic执行完之后会返回一个TaskHandle_t(句柄)类型的对象,我们可以通过这个来删除静态任务
xTaskHandle3 = xTaskCreateStatic(task3Function,"Task3",100,NULL,1,xTask3Stack,&xTask3TCB);
/* Start the scheduler. */
vTaskStartScheduler();
/* Will only get here if there was not enough heap space to create the
idle task. */
return 0;
}
Hello World
3333333333331111111111122222222222233333333333111111111111222222222223333333333331111111111122222222222233333333333111111111111222222222223333333333331111111111122222222222233333333333111111111111222222222223333333333331111111111122222222222233333333333111111111111222222222223333333333331111111111122222222222233333333333222222222222333333333332222222222223333333333322222222222233333333333222222222222333333333332222222222223333333333322222222222233333333333222222222222333333333332222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222
//句柄(可以理解为一个任务的唯一标识)
TaskHandle_t xTaskHandle1; //创建句柄
TaskHandle_t xTaskHandle3; //创建句柄
//用来标记任务是否运行
static int task1flagrun = 0;
static int task2flagrun = 0;
static int task3flagrun = 0;
/*-----------------------------------------------------------*/
void task1Function(void * Parameters)
{
while(1)
{
task1flagrun = 1;
task2flagrun = 0;
task3flagrun = 0;
printf("1");
}
}
void task2Function(void * Parameters)
{
int i;
while(1)
{
task1flagrun = 0;
task2flagrun = 1;
task3flagrun = 0;
printf("2");
if(i++ == 100)
{
vTaskDelete(xTaskHandle1); //将任务1删除
}
if(i == 200)
{
vTaskDelete(xTaskHandle3); //将任务3删除
}
if(i == 300)
{
vTaskDelete(NULL); //将任务2删除(自杀)
}
}
}
void task3Function(void * Parameters)
{
while(1)
{
task1flagrun = 0;
task2flagrun = 0;
task3flagrun = 1;
printf("3");
}
}
//创建通用函数
void taskGenericFunction(void * Para)
{
int val = (int)Para;
while(1)
{
printf("%d",val);
}
}
/*-----------------------------------------------------------*/
//创建一个栈空间
StackType_t xTask3Stack[100]; //这里100的话深度(usStackDepth)也得写100
//创建TCB
StaticTask_t xTask3TCB;
//创建一个空闲任务
StackType_t xIdleTaskStack[100];
StaticTask_t xIdleTaskTCB;
//想要调用xTaskCreateStatic的话得要实现vApplicationGetIdleTaskMemory函数
void vApplicationGetIdleTaskMemory( StaticTask_t ** ppxIdleTaskTCBBuffer,
StackType_t ** ppxIdleTaskStackBuffer,
uint32_t * pulIdleTaskStackSize )
{
*ppxIdleTaskTCBBuffer = &xIdleTaskTCB;
*ppxIdleTaskStackBuffer = xIdleTaskStack;
*pulIdleTaskStackSize = 100;
}
int main( void )
{
#ifdef DEBUG
debug();
#endif
prvSetupHardware();
printf("Hello World\r\n");
xTaskCreate(task1Function,"Task1",100,NULL,1,&xTaskHandle1); //创建任务1
xTaskCreate(task2Function,"Task2",100,NULL,1,NULL); //创建任务2
//xTaskCreateStatic执行完之后会返回一个TaskHandle_t(句柄)类型的对象,我们可以通过这个来删除静态任务
xTaskHandle3 = xTaskCreateStatic(task3Function,"Task3",100,NULL,1,xTask3Stack,&xTask3TCB);
//使用同一个任务函数创建多个任务
xTaskCreate(taskGenericFunction,"Task4",100,(void *)4,1,NULL);
xTaskCreate(taskGenericFunction,"Task5",100,(void *)5,1,NULL);
/* Start the scheduler. */
vTaskStartScheduler();
/* Will only get here if there was not enough heap space to create the
idle task. */
return 0;
}
为什么同一个函数能够创建不同的任务呢?
栈的分配操作:(具体细节在内存管理章节)
我们知道栈里面会保存各种寄存器和局部变量,我们知道栈是从高地址往下增长的(绿色线),当我们使用大量的局部变量之后就会破坏掉头和TCB1以及TCB1的头部,此时就会出现不可预计的后果
void task1Function(void * Parameters)
{
//volatile:确保本条指令不会因编译器的优化而省略,且要求每次直接读值
//因为创建任务1的时候传入的栈值是100( 100*4 = 400),这里故意写大一点,让buf把占空间耗尽
volatile char buf[500];
int i;
while(1)
{
task1flagrun = 1;
task2flagrun = 0;
task3flagrun = 0;
printf("1");
for (i=0; i<500; i++)
buf[i] = 0; //把栈空间的值清零,此时会把TCB也破坏掉
}
}
void task2Function(void * Parameters)
{
while(1)
{
task1flagrun = 0;
task2flagrun = 1;
task3flagrun = 0;
printf("2");
}
}
void task3Function(void * Parameters)
{
while(1)
{
task1flagrun = 0;
task2flagrun = 0;
task3flagrun = 1;
printf("3");
}
}
//创建通用函数
void taskGenericFunction(void * Para)
{
int val = (int)Para;
while(1)
{
printf("%d",val);
}
}
/*-----------------------------------------------------------*/
//创建一个栈空间
StackType_t xTask3Stack[100]; //这里100的话深度(usStackDepth)也得写100
//创建TCB
StaticTask_t xTask3TCB;
//创建一个空闲任务
StackType_t xIdleTaskStack[100];
StaticTask_t xIdleTaskTCB;
//想要调用xTaskCreateStatic的话得要实现vApplicationGetIdleTaskMemory函数
void vApplicationGetIdleTaskMemory( StaticTask_t ** ppxIdleTaskTCBBuffer,
StackType_t ** ppxIdleTaskStackBuffer,
uint32_t * pulIdleTaskStackSize )
{
*ppxIdleTaskTCBBuffer = &xIdleTaskTCB;
*ppxIdleTaskStackBuffer = xIdleTaskStack;
*pulIdleTaskStackSize = 100;
}
int main( void )
{
#ifdef DEBUG
debug();
#endif
prvSetupHardware();
printf("Hello World\r\n");
xTaskCreate(task1Function,"Task1",100,NULL,1,&xTaskHandle1); //创建任务1
xTaskCreate(task2Function,"Task2",100,NULL,1,NULL); //创建任务2
//xTaskCreateStatic执行完之后会返回一个TaskHandle_t(句柄)类型的对象,我们可以通过这个来删除静态任务
xTaskHandle3 = xTaskCreateStatic(task3Function,"Task3",100,NULL,1,xTask3Stack,&xTask3TCB);
//使用同一个任务函数创建多个任务
xTaskCreate(taskGenericFunction,"Task4",100,(void *)4,1,NULL);
xTaskCreate(taskGenericFunction,"Task5",100,(void *)5,1,NULL);
/* Start the scheduler. */
vTaskStartScheduler();
/* Will only get here if there was not enough heap space to create the
idle task. */
return 0;
}
HardFault_Handler硬件错误
注意:
这里的时钟是72MHz,并且PLLCLK9倍频,所以要把外部晶振设置为8MHz
设置方法:
//句柄(可以理解为一个任务的唯一标识)
TaskHandle_t xTaskHandle1; //创建句柄
TaskHandle_t xTaskHandle3; //创建句柄
//用来标记任务是否运行
static int task1flagrun = 0;
static int task2flagrun = 0;
static int task3flagrun = 0;
/*-----------------------------------------------------------*/
void task1Function(void * Parameters)
{
//每次进入中断TickCount就会+1
TickType_t tStart = xTaskGetTickCount(); //记录开始时间
TickType_t t;
int flag = 0; //标记是否进入暂停
printf("打印了1次\r\n");
while(1)
{
t = xTaskGetTickCount(); //记录当前时间
task1flagrun = 1;
task2flagrun = 0;
task3flagrun = 0;
printf("1");
if( !flag && (t >= tStart + 10) )
{
//如果时间大于10的话,就让Task3进入暂停状态
vTaskSuspend(xTaskHandle3);
flag = 1;
}
if( t >= tStart + 20)
{
//让Task从暂停状态回到准备状态
vTaskResume( xTaskHandle3 );
}
}
}
void task2Function(void * Parameters)
{
while(1)
{
task1flagrun = 0;
task2flagrun = 1;
task3flagrun = 0;
printf("2");
//进入等待状态(Blocked) ,等待10个Tick的时间
vTaskDelay(10);
}
}
void task3Function(void * Parameters)
{
while(1)
{
task1flagrun = 0;
task2flagrun = 0;
task3flagrun = 1;
printf("3");
}
}
/*-----------------------------------------------------------*/
//创建一个栈空间
StackType_t xTask3Stack[100]; //这里100的话深度(usStackDepth)也得写100
//创建TCB
StaticTask_t xTask3TCB;
//创建一个空闲任务
StackType_t xIdleTaskStack[100];
StaticTask_t xIdleTaskTCB;
//想要调用xTaskCreateStatic的话得要实现vApplicationGetIdleTaskMemory函数
void vApplicationGetIdleTaskMemory( StaticTask_t ** ppxIdleTaskTCBBuffer,
StackType_t ** ppxIdleTaskStackBuffer,
uint32_t * pulIdleTaskStackSize )
{
*ppxIdleTaskTCBBuffer = &xIdleTaskTCB;
*ppxIdleTaskStackBuffer = xIdleTaskStack;
*pulIdleTaskStackSize = 100;
}
int main( void )
{
#ifdef DEBUG
debug();
#endif
prvSetupHardware();
printf("Hello World\r\n");
xTaskCreate(task1Function,"Task1",100,NULL,1,&xTaskHandle1); //创建任务1
xTaskCreate(task2Function,"Task2",100,NULL,1,NULL); //创建任务2
//xTaskCreateStatic执行完之后会返回一个TaskHandle_t(句柄)类型的对象,我们可以通过这个来删除静态任务
xTaskHandle3 = xTaskCreateStatic(task3Function,"Task3",100,NULL,1,xTask3Stack,&xTask3TCB);
/* Start the scheduler. */
vTaskStartScheduler();
/* Will only get here if there was not enough heap space to create the
idle task. */
return 0;
}
当Task1累加到10次tick的时候,下一次运行将会把Task3变为暂停状态(Suspend)
对于Task2会在第一次运行的时候就进入阻塞状态(Blocked),当等待10个tick的时候重新返回准备状态(Ready)
有两个Delay函数:
如图,vTaskDelay是固定等待时间 N * Tick,如前面有一个不固定时间的程序,那么t1 到 t2 的时间就不是固定的
如图,从t1 到 t2 的时间称为△t ,当调用vTaskDelayUntil时传入t1 和 △t(终点),那么无论vTaskDelayUntil在t1的什么时候调用,t1 到 t2的时间都是不变的,变得只是阻塞时间(Blocked)
总结:
//句柄(可以理解为一个任务的唯一标识)
TaskHandle_t xTaskHandle1; //创建句柄
TaskHandle_t xTaskHandle3; //创建句柄
//用来标记任务是否运行
static int task1flagrun = 0;
static int task2flagrun = 0;
static int task3flagrun = 0;
/*-----------------------------------------------------------*/
//随机值,用来让Task1的执行时间不固定
static int radns[] = {3,53,45,110,12};
/*-----------------------------------------------------------*/
void task1Function(void * Parameters)
{
//每次进入中断TickCount就会+1
TickType_t tStart = xTaskGetTickCount(); //记录T1(此代码只会执行一次)
int i,j=0;
while(1)
{
task1flagrun = 1;
task2flagrun = 0;
task3flagrun = 0;
for(i=0; i<radns[j]; i++)
printf("1");
j++;
if(j == 5)
j = 0;
#if 0
vTaskDelay(8); //阻塞(等待)8个Tick
#else
/*
vTaskDelayUntil( pxPreviousWakeTime, xTimeIncrement )
1、直到*pxPr + △T时刻,该函数才会从Blocked状态退出
2、更新*pxPr:*pxPr(之前的T) + △T
*/
vTaskDelayUntil(&tStart,8);
#endif
}
}
void task2Function(void * Parameters)
{
while(1)
{
task1flagrun = 0;
task2flagrun = 1;
task3flagrun = 0;
printf("2");
}
}
void task3Function(void * Parameters)
{
while(1)
{
task1flagrun = 0;
task2flagrun = 0;
task3flagrun = 1;
printf("3");
}
}
/*-----------------------------------------------------------*/
//创建一个栈空间
StackType_t xTask3Stack[100]; //这里100的话深度(usStackDepth)也得写100
//创建TCB
StaticTask_t xTask3TCB;
//创建一个空闲任务
StackType_t xIdleTaskStack[100];
StaticTask_t xIdleTaskTCB;
//想要调用xTaskCreateStatic的话得要实现vApplicationGetIdleTaskMemory函数
void vApplicationGetIdleTaskMemory( StaticTask_t ** ppxIdleTaskTCBBuffer,
StackType_t ** ppxIdleTaskStackBuffer,
uint32_t * pulIdleTaskStackSize )
{
*ppxIdleTaskTCBBuffer = &xIdleTaskTCB;
*ppxIdleTaskStackBuffer = xIdleTaskStack;
*pulIdleTaskStackSize = 100;
}
int main( void )
{
#ifdef DEBUG
debug();
#endif
prvSetupHardware();
printf("Hello World\r\n");
//任务1的优先级最高
xTaskCreate(task1Function,"Task1",100,NULL,2,&xTaskHandle1); //创建任务1
xTaskCreate(task2Function,"Task2",100,NULL,1,NULL); //创建任务2
xTaskHandle3 = xTaskCreateStatic(task3Function,"Task3",100,NULL,1,xTask3Stack,&xTask3TCB);
/* Start the scheduler. */
vTaskStartScheduler();
/* Will only get here if there was not enough heap space to create the
idle task. */
return 0;
}
当调用vTaskDelay函数时:
当调用vTaskDelayUntil函数时:
void task2Function(void * Parameters);
void task1Function(void * Parameters)
{
TaskHandle_t xTaskHandle2; //Task2的Handle,用来删除Task2
BaseType_t xReturn;
while(1)
{
printf("1");
//当xTaskCreate创建成功后,会返回pdPASS
xReturn = xTaskCreate(task2Function,"Task2",1024,NULL,2,&xTaskHandle2);
if( xReturn != pdPASS ) //创建失败,唯一原因只有内存不够(堆不够)
printf("xTaskCreate err");
//因为Task2刚创建完,就会立马执行(优先级高),所以在这里调用删除函数,Task2也会执行至少一次的
vTaskDelete(xTaskHandle2); //干掉Task2
}
}
void task2Function(void * Parameters)
{
while(1)
{
printf("2");
//因为Task2的优先级比较高,所以想要在Task1中删除Task2就必须让Task2进入阻塞或者暂停状态
vTaskDelay(2); //休息2个Tick
}
}
/*-----------------------------------------------------------*/
int main( void )
{
//句柄(可以理解为一个任务的唯一标识)
TaskHandle_t xTaskHandle1; //创建句柄
#ifdef DEBUG
debug();
#endif
prvSetupHardware();
printf("Hello World\r\n");
xTaskCreate(task1Function,"Task1",100,NULL,1,&xTaskHandle1);
/* Start the scheduler. */
vTaskStartScheduler();
/* Will only get here if there was not enough heap space to create the
idle task. */
return 0;
}
理论:
事实:
void task2Function(void * Parameters);
void task1Function(void * Parameters)
{
TaskHandle_t xTaskHandle2; //Task2的Handle,用来删除Task2
BaseType_t xReturn;
while(1)
{
printf("1");
//当xTaskCreate创建成功后,会返回pdPASS
xReturn = xTaskCreate(task2Function,"Task2",1024,NULL,2,&xTaskHandle2);
if( xReturn != pdPASS ) //创建失败,唯一原因只有内存不够
printf("xTaskCreate err");
//因为Task2刚创建完,就会立马执行(优先级高),所以在这里调用删除函数,Task2也会执行至少一次的
//vTaskDelete(xTaskHandle2); //干掉Task2 (不会出现内存不够)
}
}
void task2Function(void * Parameters)
{
while(1)
{
printf("2");
//因为Task2的优先级比较高,所以想要在Task1中删除Task2就必须让Task2进入阻塞或者暂停状态
//vTaskDelay(2); //休息2个Tick (不会出现内存不够)
/*
内存不够原因:
如果一个任务删除自己,它就不能完成一些清理工作,必须由空闲任务来完成清理工作
*/
vTaskDelete(NULL); //自杀(会出现内存不够)
}
}
/*-----------------------------------------------------------*/
int main( void )
{
//句柄(可以理解为一个任务的唯一标识)
TaskHandle_t xTaskHandle1; //创建句柄
#ifdef DEBUG
debug();
#endif
prvSetupHardware();
printf("Hello World\r\n");
xTaskCreate(task1Function,"Task1",100,NULL,1,&xTaskHandle1);
/* Start the scheduler. */
vTaskStartScheduler();
/* Will only get here if there was not enough heap space to create the
idle task. */
return 0;
}
可以看到程序很快就出现了内存不够的情况
空闲任务钩子函数
使用钩子函数的前提:
static int task1Flagrun = 0;
static int task2Flagrun = 0;
static int taskidleFlagrun = 0;
void task2Function(void * Parameters);
void task1Function(void * Parameters)
{
TaskHandle_t xTaskHandle2; //Task2的Handle,用来删除Task2
BaseType_t xReturn;
while(1)
{
task1Flagrun = 1;
task2Flagrun = 0;
taskidleFlagrun = 0;
printf("1");
xReturn = xTaskCreate(task2Function,"Task2",1024,NULL,2,&xTaskHandle2);
if( xReturn != pdPASS ) //创建失败,唯一原因只有内存不够
printf("xTaskCreate err\r\n");
}
}
void task2Function(void * Parameters)
{
while(1)
{
task1Flagrun = 0;
task2Flagrun = 1;
taskidleFlagrun = 0;
printf("2");
vTaskDelete(NULL); //自杀(会出现内存不够)
}
}
void vApplicationIdleHook( void )
{
task1Flagrun = 0;
task2Flagrun = 0;
taskidleFlagrun = 1;
printf("0");
}
/*-----------------------------------------------------------*/
int main( void )
{
//句柄(可以理解为一个任务的唯一标识)
TaskHandle_t xTaskHandle1; //创建句柄
#ifdef DEBUG
debug();
#endif
prvSetupHardware();
printf("Hello World\r\n");
xTaskCreate(task1Function,"Task1",100,NULL,0,&xTaskHandle1);
/* Start the scheduler. */
vTaskStartScheduler(); //此处会创建空闲任务
/* Will only get here if there was not enough heap space to create the
idle task. */
return 0;
}
正在运行的任务,被称为"正在使用处理器",它处于运行状态。在单处理器系统中,任何时间里只能有一个任务处于运行状态。
非运行状态的任务,它处于这3种状态之一:
就绪态的任务,可以被调度器挑选出来切换为运行状态,调度器永远都是挑选最高优先级的就绪态任务并让它进入运行状态。
阻塞状态的任务,它在等待"事件",当事件发生时任务就会进入就绪状态。
事件分为两类:
从3个角度统一理解多种调度算法:
列表如下:
配置项 | A | B | C | D | E |
---|---|---|---|---|---|
configUSE_PREEMPTION | 1 | 1 | 1 | 1 | 0 |
configUSE_TIME_SLICING | 1 | 1 | 0 | 0 | x |
configIDLE_SHOULD_YIELD | 1 | 0 | 1 | 0 | x |
说明 | 常用 | 很少用 | 很少用 | 很少用 | 几乎不用 |
注:
配置此配置项可决定是否可抢占
static volatile int flagIdleTaskrun = 0; // 空闲任务运行时flagIdleTaskrun=1
static volatile int flagTask1run = 0; // 任务1运行时flagTask1run=1
static volatile int flagTask2run = 0; // 任务2运行时flagTask2run=1
static volatile int flagTask3run = 0; // 任务3运行时flagTask3run=1
/*-----------------------------------------------------------*/
void vTask1( void *pvParameters )
{
/* 任务函数的主体一般都是无限循环 */
for( ;; )
{
flagIdleTaskrun = 0;
flagTask1run = 1;
flagTask2run = 0;
flagTask3run = 0;
/* 打印任务的信息 */
printf("T1\r\n");
}
}
void vTask2( void *pvParameters )
{
/* 任务函数的主体一般都是无限循环 */
for( ;; )
{
flagIdleTaskrun = 0;
flagTask1run = 0;
flagTask2run = 1;
flagTask3run = 0;
/* 打印任务的信息 */
printf("T2\r\n");
}
}
void vTask3( void *pvParameters )
{
const TickType_t xDelay5ms = pdMS_TO_TICKS( 5UL );
/* 任务函数的主体一般都是无限循环 */
for( ;; )
{
flagIdleTaskrun = 0;
flagTask1run = 0;
flagTask2run = 0;
flagTask3run = 1;
/* 打印任务的信息 */
printf("T3\r\n");
// 如果不休眠的话, 其他任务无法得到执行
vTaskDelay( xDelay5ms );
}
}
void vApplicationIdleHook(void)
{
flagIdleTaskrun = 1;
flagTask1run = 0;
flagTask2run = 0;
flagTask3run = 0;
/* 故意加入打印让flagIdleTaskrun变为1的时间维持长一点 */
//printf("Id\r\n");
}
int main( void )
{
prvSetupHardware();
xTaskCreate(vTask1, "Task 1", 1000, NULL, 0, NULL);
xTaskCreate(vTask2, "Task 2", 1000, NULL, 0, NULL);
//此处Task3的优先级最高,所以当Task3从阻塞中出来之后,就会立马执行任务(此时为可抢占)
xTaskCreate(vTask3, "Task 3", 1000, NULL, 2, NULL);
/* 启动调度器 */
vTaskStartScheduler();
/* 如果程序运行到了这里就表示出错了, 一般是内存不足 */
return 0;
}
当configUSE_PREEMPTION为0时(不可抢占):
当configUSE_PREEMPTION为1时(允许抢占):
配置此配置项可决定同优先级是否可轮流执行
static volatile int flagIdleTaskrun = 0; // 空闲任务运行时flagIdleTaskrun=1
static volatile int flagTask1run = 0; // 任务1运行时flagTask1run=1
static volatile int flagTask2run = 0; // 任务2运行时flagTask2run=1
static volatile int flagTask3run = 0; // 任务3运行时flagTask3run=1
/*-----------------------------------------------------------*/
void vTask1( void *pvParameters )
{
/* 任务函数的主体一般都是无限循环 */
for( ;; )
{
flagIdleTaskrun = 0;
flagTask1run = 1;
flagTask2run = 0;
flagTask3run = 0;
/* 打印任务的信息 */
printf("T1\r\n");
}
}
void vTask2( void *pvParameters )
{
/* 任务函数的主体一般都是无限循环 */
for( ;; )
{
flagIdleTaskrun = 0;
flagTask1run = 0;
flagTask2run = 1;
flagTask3run = 0;
/* 打印任务的信息 */
printf("T2\r\n");
}
}
void vTask3( void *pvParameters )
{
const TickType_t xDelay5ms = pdMS_TO_TICKS( 5UL );
/* 任务函数的主体一般都是无限循环 */
for( ;; )
{
flagIdleTaskrun = 0;
flagTask1run = 0;
flagTask2run = 0;
flagTask3run = 1;
/* 打印任务的信息 */
printf("T3\r\n");
// 如果不休眠的话, 其他任务无法得到执行
vTaskDelay( xDelay5ms );
}
}
void vApplicationIdleHook(void)
{
flagIdleTaskrun = 1;
flagTask1run = 0;
flagTask2run = 0;
flagTask3run = 0;
/* 故意加入打印让flagIdleTaskrun变为1的时间维持长一点 */
//printf("Id\r\n");
}
int main( void )
{
prvSetupHardware();
xTaskCreate(vTask1, "Task 1", 1000, NULL, 0, NULL);
xTaskCreate(vTask2, "Task 2", 1000, NULL, 0, NULL);
//此处Task3的优先级最高,所以当Task3从阻塞中出来之后,就会立马执行任务(此时为可抢占)
xTaskCreate(vTask3, "Task 3", 1000, NULL, 2, NULL);
/* 启动调度器 */
vTaskStartScheduler();
/* 如果程序运行到了这里就表示出错了, 一般是内存不足 */
return 0;
}
此处配置可抢占,不可轮流执行
此配置项可配置空闲任务是否礼让其他任务
ldieTask任务内部流程
IdleTask(){
while(1)
{
xxxx //做某些事情
钩子(); //调用钩子函数
if: YIELD == 1
触发一次调度
endif
}
}
static volatile int flagIdleTaskrun = 0; // 空闲任务运行时flagIdleTaskrun=1
static volatile int flagTask1run = 0; // 任务1运行时flagTask1run=1
static volatile int flagTask2run = 0; // 任务2运行时flagTask2run=1
static volatile int flagTask3run = 0; // 任务3运行时flagTask3run=1
/*-----------------------------------------------------------*/
void vTask1( void *pvParameters )
{
/* 任务函数的主体一般都是无限循环 */
for( ;; )
{
flagIdleTaskrun = 0;
flagTask1run = 1;
flagTask2run = 0;
flagTask3run = 0;
/* 打印任务的信息 */
printf("T1\r\n");
}
}
void vTask2( void *pvParameters )
{
/* 任务函数的主体一般都是无限循环 */
for( ;; )
{
flagIdleTaskrun = 0;
flagTask1run = 0;
flagTask2run = 1;
flagTask3run = 0;
/* 打印任务的信息 */
printf("T2\r\n");
}
}
void vTask3( void *pvParameters )
{
const TickType_t xDelay5ms = pdMS_TO_TICKS( 5UL );
/* 任务函数的主体一般都是无限循环 */
for( ;; )
{
flagIdleTaskrun = 0;
flagTask1run = 0;
flagTask2run = 0;
flagTask3run = 1;
/* 打印任务的信息 */
printf("T3\r\n");
// 如果不休眠的话, 其他任务无法得到执行
vTaskDelay( xDelay5ms );
}
}
void vApplicationIdleHook(void)
{
flagIdleTaskrun = 1;
flagTask1run = 0;
flagTask2run = 0;
flagTask3run = 0;
/* 故意加入打印让flagIdleTaskrun变为1的时间维持长一点 */
//printf("Id\r\n");
}
int main( void )
{
prvSetupHardware();
xTaskCreate(vTask1, "Task 1", 1000, NULL, 0, NULL);
xTaskCreate(vTask2, "Task 2", 1000, NULL, 0, NULL);
//此处Task3的优先级最高,所以当Task3从阻塞中出来之后,就会立马执行任务(此时为可抢占)
xTaskCreate(vTask3, "Task 3", 1000, NULL, 2, NULL);
/* 启动调度器 */
vTaskStartScheduler();
/* 如果程序运行到了这里就表示出错了, 一般是内存不足 */
return 0;
}
此处为configIDLE_SHOULD_YIELD = 0时,即空闲任务不礼让其他任务
此处为configIDLE_SHOULD_YIELD = 1时,即空闲任务不礼让其他任务
xxx //做某些事情
钩子(); //调用钩子函数
if: YIELD == 1
触发一次调度
endif
}
}
#### 代码
```c
static volatile int flagIdleTaskrun = 0; // 空闲任务运行时flagIdleTaskrun=1
static volatile int flagTask1run = 0; // 任务1运行时flagTask1run=1
static volatile int flagTask2run = 0; // 任务2运行时flagTask2run=1
static volatile int flagTask3run = 0; // 任务3运行时flagTask3run=1
/*-----------------------------------------------------------*/
void vTask1( void *pvParameters )
{
/* 任务函数的主体一般都是无限循环 */
for( ;; )
{
flagIdleTaskrun = 0;
flagTask1run = 1;
flagTask2run = 0;
flagTask3run = 0;
/* 打印任务的信息 */
printf("T1\r\n");
}
}
void vTask2( void *pvParameters )
{
/* 任务函数的主体一般都是无限循环 */
for( ;; )
{
flagIdleTaskrun = 0;
flagTask1run = 0;
flagTask2run = 1;
flagTask3run = 0;
/* 打印任务的信息 */
printf("T2\r\n");
}
}
void vTask3( void *pvParameters )
{
const TickType_t xDelay5ms = pdMS_TO_TICKS( 5UL );
/* 任务函数的主体一般都是无限循环 */
for( ;; )
{
flagIdleTaskrun = 0;
flagTask1run = 0;
flagTask2run = 0;
flagTask3run = 1;
/* 打印任务的信息 */
printf("T3\r\n");
// 如果不休眠的话, 其他任务无法得到执行
vTaskDelay( xDelay5ms );
}
}
void vApplicationIdleHook(void)
{
flagIdleTaskrun = 1;
flagTask1run = 0;
flagTask2run = 0;
flagTask3run = 0;
/* 故意加入打印让flagIdleTaskrun变为1的时间维持长一点 */
//printf("Id\r\n");
}
int main( void )
{
prvSetupHardware();
xTaskCreate(vTask1, "Task 1", 1000, NULL, 0, NULL);
xTaskCreate(vTask2, "Task 2", 1000, NULL, 0, NULL);
//此处Task3的优先级最高,所以当Task3从阻塞中出来之后,就会立马执行任务(此时为可抢占)
xTaskCreate(vTask3, "Task 3", 1000, NULL, 2, NULL);
/* 启动调度器 */
vTaskStartScheduler();
/* 如果程序运行到了这里就表示出错了, 一般是内存不足 */
return 0;
}
此处为configIDLE_SHOULD_YIELD = 0时,即空闲任务不礼让其他任务
此处为configIDLE_SHOULD_YIELD = 1时,即空闲任务不礼让其他任务