FreeRTOS学习记录第五天

一、延迟中断处理
主函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
static void AppTaskCreate (void)
{
/*信号量在使用前必须先创建。本例中创建了一个二值信号量*/
vSemaphoreCreateBinary(xBinarySemaphore);
/*检测信号量是否成功创建*/
if(xBinarySemaphore!=NULL)
{
/*创建延迟处理任务。此任务将与中断同步。延迟处理任务在创建时用了一个较高的
优先级,以保证中断退出后立即执行。(在本例中由于只有一个任务所以没有必要)*/
xTaskCreate(vHandlerTask,"Handler",1000,NULL,3,NULL);
/*启动调度器*/
vTaskStartScheduler();
}
}

中断回调函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
static portBASE_TYPE xHigherPriorityTaskWoken;
xHigherPriorityTaskWoken=pdFALSE;
/*给出信号量来解除任务阻塞*/
xSemaphoreGiveFromISR(xBinarySemaphore,&xHigherPriorityTaskWoken);
if(xHigherPriorityTaskWoken==pdTRUE)
{
/*给出信号量以使得等待此信号量的任务接触阻塞。如果解除阻塞的任务的
优先级高于当前任务的优先级-强制进行上下文切换,以确保中断直接返回到
解除阻塞的任务(优先级更高)*/
portYIELD_FROM_ISR();
}
}

同步任务:

1
2
3
4
5
6
7
8
9
10
11
12
13
static void vHandlerTask( void *pvParameters )
{

for( ;; )
{
/*使用信号量等待一个事件。信号量在调度器启动之前,也即此任务之前就已被创建。
任务无超时阻塞,所以此函数调用也只有会在成功获取信号量之后才会返回。此处也
没有必要检测返回值*/
xSemaphoreTake(xBinarySemaphore,portMAX_DELAY);
/*程序运行到这里,事件必然已经发生*/
printf("Handler task-Processing event\r\n");
}
}

FreeRTOS学习记录第六天

写队列任务实现代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
static void vIntegerGenerator(void *pvParameters)
{
portTickType xLastExecutionTime;
unsigned portLONG u1ValueToSend=0;
int i;
/*初始化变量,用于调用vTaskDelayUntil(),用于准确延时*/
xLastExecutionTime=xTaskGetTickCount();
for(;;)
{
/*这是个周期性任务。进入阻塞态直到再次运行的时刻。此任务每200毫秒执行一次*/
vTaskDelayUntil(&xLastExecutionTime,200/portTICK_RATE_MS);
/*连续五次发送递增数值到队列。此数值将在中断服务程序中读出。中断服务例程会
将队列读空,所以此任务可以确保将所有的数值都发送到队列。因此不需要设置阻塞
时间*/
for(i=0;i<5;i++)
{
xQueueSendToBack(xIntegerQueue,&u1ValueToSend,0);
u1ValueToSend++;
}
/*产生中断,以让中断服务例程读取队列*/
printf("About to generate an interrupt\r\n");
__asm{int 0x82}
printf("Interrupt generated\r\n");
}
}

中断服务例程实现代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
static void __interrupt__far vExampleInterruptHandle(void)
{
static portBASE_TYPE xHigherPriorityTaskWoken;
static unsigned long u1ReceivedNumber;
/*这些字符串被声明为static const,以保证它们不会被定位到ISR的栈空间中,即使ISR
没有运行它们也是存在的*/
static const char *pcStrings[]=
{
"String 0\r\n",
"String 1\r\n",
"String 2\r\n",
"String 3\r\n"
};
xHigherPriorityTaskWoken=pdFALSE;
/*重复执行,直到队列为空*/
while(xQueueReceiveFromISR(xIntegerQueue,&u1ReceivedNumber,&xHigherPriorityTaskWoken)!=errQUEUE_EMPTY)
{
/*截取收到的数据,保留低两位(数值范围0到3),然后将索引到的字符串指针发送到另一个队列*/
u1ReceivedNumber&=0x03;
xQueueSendToBackFromISR(xStringQueue,&pcStrings[u1ReceivedNumber],&xHigherPriorityTaskWoken);
}
/*被队列读写操作解除阻塞的任务,其优先级是否高于当前任务?如果是,则进行上下文切换*/
if(xHigherPriorityTaskWoken==pdTRUE)
{
/*给出信号量以使得等待此信号量的任务接触阻塞。如果解除阻塞的任务的
优先级高于当前任务的优先级-强制进行上下文切换,以确保中断直接返回到
解除阻塞的任务(优先级更高)*/
portYIELD_FROM_ISR();
}
}

字符串接收任务实现,其接收来自中断服务例程的字符串,并打印输出:

1
2
3
4
5
6
7
8
9
10
static void vStringPrinter(void *pvParameters)
{
char *pcString;
for(;;)
{
/*读队列阻塞,直到有消息到来,并将接收的字符串打印输出*/
xQueueReceive(xStringQueue,&pcString,portMAX_DELAY);
printf("%s",pcString);
}
}

main()函数实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
int main(void)
{
/*队列使用前必须先创建。本例中创建了两个队列。一个队列用于保存类型为unsigned long
的变量,另一个队列用于保存类型为char*的变量。两个队列的深度都为10。在实际应用中应
检测返回值以确保队列创建成功*/
xIntegerQueue=xQueueCreate(10,sizeof(unsigned long));
xStringQueue=xQueueCreate(10,sizeof(char*));
/*安装中断服务例程*/
_dos_setvect(0x82,vExampleInterruptHandle);
/*创建任务用于往中断服务例程中发送数值。此优先级为1*/
xTaskCreate(vIntegerGenerator,"IntGen",1000,NULL,1,NULL);
/*创建任务用于从中断服务例程中接收字符串,并打印输出。此任务的优先级为2*/
xTaskCreate(vStringPrinter,"String",1000,NULL,2,NULL);
/*开启调度器*/
vTaskStartScheduler();
/* 如果一切正常, main()函数不应该会执行到这里。但如果执行到这里,很可能是内存堆空间不足导致空闲
任务无法创建。*/
for(;;);
}

运行输出如下:

1
2
3
4
5
6
7
8
9
10
11
12
String 3
String 0
String 1
Interrupt generated

About to generate an interrupt
String 2
String 3
String 0
String 1
String 2
Interrupt generated
注:因为这几次的实验不太方便演示,这里只是做个学习记录和理解,并没有实际运行。