基于Arduino Due的FreeRTOS程序设计(AtmelSAM3X8E)之二

来源:互联网 发布:licecap for mac 下载 编辑:程序博客网 时间:2024/05/17 01:46

接上一篇

3 连接开发硬件平台到IDE,并建立相关的工程

本例中使用的IDE是ATMEL公司的AtmelStudio 7.0 ,如下图所示。这个IDE是一个基于Visual Studio应用布局模板的IDE,相信用过Visual Studio的读者会很快上手。

在接下来的内容里,我们会以Atmel Studio作为主要的系统开发平台,来介绍如下的内容:

  • l  使用ASF向导在工程文件中添加内核
  • l  配置FreeRTOS的内核
  • l  按照实际的需求优化内核的体积

3.1 向现存的工程中添加内核

FreeRTOS内核作为Atmel SoftwareFramework(ASF)的标准组件可以很方便的加入到项目文件中区,在本例中,将以SAM3X系列的处理器为例,演示具体的操作步骤。

  • 首先选择NewProject,在New Project的项目模板中选择C/C++项目类型,在右侧选择GCC ASF Board Project。并在Name中输入项目名称,这里使用缺省的工程名,并点击OK按钮。

  • 选择开发板的处理器和开发板的型号(本例中使用的是SAM3X8E,开发板为Arduino DUE)

Atmel Studio提供了一个非常全面的选择界面。可以使用“Select by Device”或者“Select by board”两种方式来选择。在Device列表中选择相应的芯片,如ATSAM3X8E。在选择了支持的芯片以后,下方会出现相关的开发板。选择开发板以后,右侧可以查看Device(芯片)或者开发板的详细信息。在完成选择之后,点击OK按钮继续下一步。

  • 添加FreeRTOS的核心

Atmel studio会自动打开ASF向导,让用户添加可用模块。在1中选择当前的项目,并在2处搜索“FreeRTOS”,IDE会过滤出所有和FreeRTOS相关的模块。如3处所示。这里选择FreeRTOS mini Real-Time Kernel选项,并选择添加,这样一个最小的FreeRTOS内核就被加入到项目的工程文件中去了。除了FreeRTOS mini Real-Time kernel,还有其他的几个以“FreeRTOS”为前缀的模块名称,通过名称,可以推断它们是一些接口的驱动。点击展开查看其内部的内容,本质上这些模块是一堆接口定义的头文件,在被加入系统后,系统会加入相对应的驱动源程序,用以完成整个系统的编译。观察FreeRTOS-Mini Real-Time kernel里的内容,也是一堆头文件,例如queue.h、semphr.h、timer.h等,这些都是一个实时系统不可缺少的队列、同步以及时钟等等功能的定义。再加入“FreeRTOS Mini Real-Time kernel”模块以后,系统全部的模块,可以通过“Selectedmodules”列表查看。

图 全部的模块列表

最后点击“Apply”按钮。

在点击“Apply”之后,会出现一个对话框,里面列举了所有要插入项目的头文件名称。我们刚才提到了Mini Real-time内核里的那些头文件也包含在其中(如红线标识所示)。此时,点击“OK”按钮,选择接受“FreeTROS mini real-time kernel”license的Checkbox,点击“Finish”按钮,并最后完成。

图插入FreeRTOS mini real-time 内核文件以后的工程规模

3.2 配置内核

内核的配置是通过修改文件FreeRTOSConifg.h中的预定义的“config***”或“INCLUDE”预编译语句完成的。打开这个文件,我们可见到很多的#Define语句,他们定义了将要被编译进入FreeRTOS内核的接口函数。其中,有一些配置可以通过设置预定义的值为“0”或者“1”来完成,例如:“#defineconfigUSE_PREEMPTION        1”。这些预定义值得具体的具体含义可以参考freertos的官方的介绍说明文档:https://www.freertos.org/a00110.html。

3.2.1 系统和核心运行频率

值得注意的是,内核定义的处理器频率和核心运行频率,在FreeRTOSConfig.h中的定义如下:

#define configCPU_CLOCK_HZ     ( sysclk_get_cpu_hz() )
#define configTICK_RATE_HZ         ( ( portTickType ) 1000 )

configCPU_CLOCK_HZ通过函数sysclk_get_cpu_hz()完成设置;configTICK_RATE_HZ设置成portTickType类型的数值,缺省值为1000Hz。其中portTickType是一个个用户自定义数据类型,其数据长度也是依据配置文件中的预定义值configUSE_16_BIT_TICKS来完成的。

#if( configUSE_16_BIT_TICKS == 1 )
    typedef unsigned portSHORT portTickType;
    #define portMAX_DELAY ( portTickType ) 0xffff
#else
    typedef unsigned portLONG portTickType;
    #define portMAX_DELAY ( portTickType ) 0xffffffff
#endi

此外,要确保sysclk_get_cpu_hz()函数运行成功,函数sysclk_int()函数需要在main函数中先被执行。缺省的main.c里面并没有这个sysclk_init函数,需要手动添加进去。


3.2.2 任务的建立和调度

任务的结构是一个没有返回值的函数,通常在函数体内实现一个不间断的循环,来完成一个任务。通常一个任务的定义如下:

void vATaskFunction(void *pvParameters)

{

for( ;; )

{

/*Task application code here.*/

}

}

因为没有返回值,所以这个任务被定义为void类型。并且可以使用自定义类型来定义向任务内部传递参数的数据结构,例如:

Typedef struct {

const char Parameter1;

const uint32_t Parameter2;

/*...*/

} pvParameters;

为了执行一个任务,任务需要被事先创建(分配内存并将任务加入调度列表)。在穿件一个任务的时候,系统会给这个任务分配一个ID号,这个ID号还会被用作内和任务管理函数的参数。

xTaskHandle task_handle_ID

创建任务使用函数XTaskCreate(),删除一个任务使用vTaskDelete()函数。

 

3.3 任务的创建和删除

3.3.1 创建任务

xTaskCreate函数在内存中为任务分配空间,其参数用来设置任务的名称、堆深度、任务的优先级和任务的实现代码(函数)等。其原型如下:

Void xTaskCreate(pvTaskCode,pcName, usStackDepth, pvParameters, uxPriority,

pxCreatedTask;

PvTaskCode:任务的实现函数

PcName:任务的名称(仅在调试时使用)

UsStackDepth:任务的堆的大小(单位字)

PvParameters:传递给任务的用户自定义结构体的指针

UxPriority:任务的优先级(从 0 MAX_PRIORITIES– 1

PxCreatedTask:Pointer to an identifier that allows handling the task. If the task does nothave to be handled in the future, this can be NULL

3.3.2 删除任务

函数vTaskDelete用来删除一个已存在的任务。要确保使用INCLUDE_vTaskDelete为“1”,以便将函数编译入系统。vTaskDelete函数会将任务移出任务管理队列,并释放任务的一切资源。任务标识符被用来当做函数的参数,来指明要删除的任务到底是哪一个。其函数原型如下:

void vTaskDelete(xTaskHandlepxTask);

PxTask:指向任务标识符的指针,用来指明要删除的任务。为空则会致使调用函数的任务被删除。当一个任务被删除以后,系统的idle任务负责删除由内核分配的一切资源,但动态分配的内存资源则需要手动删除。对任务的删除尽可能避免,因为删除一个任务会造成堆操作的加剧,进一步会影响处理器的功效。最好采用的办法是让不需要工作的任务处于休眠状态,在需要的时候通过事件加以唤醒。下面的Demo掩饰了如何使用上述的两个函数。

#include <asf.h>

/* Task handler declaration*/

xTaskHandle worker1_id;

static void worker1_task(void*pvParameters)

{

for(;;)

{

/* task application*/

}

/* Should never go there */

vTaskDelete(NULL);

}

 

Int main (void)

{

Sysclk_init();

Board_init();

/* Create Worker 1 task */

xTaskCreate(worker1_task,"Worker1",configMINIMAL_STACK_SIZE+1000,NULL, 2,& worker1_id);

/*Start Scheduler*/

vTaskStartScheduler()

while(1);

}

 

主函数在初始化以后(Sysclk_init()Board_init()执行之后),XTaskCreate创建任务,名称“Worker 1”,其优先级为2,用worker1_id标识。在work1_task任务的内部,是一个什么都不做的无限循环。在任务内部最后的一行代码是vTaskDelete函数,它的参数传递了NULL,如果有某种条件可以使任务的执行从for(;;)循环中跳出来,那么这个vTaskDelete函数才得以执行,它将深处任务本身(即删除worker1_task任务)。

 

3.3.3 Task的管理

FreeRTOS提供了多种管理任务的函数,这些函数可以设置或者获取每人任务的状态。这里建安列举一下任务管理函数。

  • VTaskDelay: /*Delay a task for a set number of tick */
  • VTaskDelayUntil/*Delay a task for a set number of tick */
  • VTaskPrioritySet/*Set task priority */
  • UxTaskPriorityGet/*Retrieve Task priority setting */
  • VTaskSuspend/*Suspend a Task */
  • VTaskResume/*Resume a Task */
  • ETaskStateGet/*Retrieve the current status of a Task */
  • VTaskDelete/*Delete a Task */

关于这些函数的使用,我们可以看看如下的示例,我们首先设计两个任务函数如下:

#include <asf.h>
xTaskHandle worker1_id;
xTaskHandle worker2_id;
static void worker1_task(void *pvParameters)
{
static uint32_t idelay;
static uint32_t Delay;
Delay = 100000;
/* Worker task Loop. */
for(;;)
{
/* Simulate work */
for (idelay = 0; idelay < Delay; ++idelay);
/* Suspend Task */
vTaskSuspend(worker1_id);
}
/* Should never go there */
vTaskDelete(worker1_id);
}

static void worker2_task(void *pvParameters)
{
static uint32_t idelay;
static uint32_t Delay;
Delay = 100000;
/* Worker task Loop. */
for(;;)
{
/* Simulate CPU work */
for (idelay = 0; idelay < Delay; ++idelay);
/* Suspend Task */
vTaskSuspend(worker2_id);
}
/* Should never go there */
vTaskDelete(worker2_id);
}

Int main (void)
{
Sysclk_init();
Board_init();
xTaskCreate(worker1_task,"Worker 1",configMINIMAL_STACK_SIZE+1000,NULL, 2,& worker1_id);
/* Create Worker 2 task */
xTaskCreate(worker2_task,"Worker 2",configMINIMAL_STACK_SIZE+1000,NULL, 1,& worker2_id);
/*Start Scheduler*/
vTaskStartScheduler()
while(1);
}

我们可以把这段示例代码拷贝到AtmelStudio中,编译后由J-Link调试器加载到目标开发板,试运行。在第一次编译的稍后可能会出现类似的错误,这是因为对vApplicationTickHook的引用问题。


编译时报错


导致错误的位置

故在FreeRTOSConfig.h文件中将#defineconfigUSE_TICK_HOOK设置为“0”即可。使用Tracelyzer工具查看生成的dump文件,可以看到Task1Task2被调度运行的情形。

 

未完待续

软通大学王明浩

阅读全文
0 0