在开发实际应用系统时,我们经常需要考虑数据的实时性和多任务,嵌入式实时操作系统的出现为实现这一目的提供了很好的助力。FreeRTOS是近年来比较流行的嵌入式实时操作系统,而且是开源免费的,STM32CubeMX对它也提供了支持。我们可以使用STM32CubeMX很方便的添加上FreeRTOS,只需要在配置界面找到“MiddleWares”并将其下的FreeRTOS选为“Enable”就可添加上FreeRTOS:

配置完成后,更新源码就会在软件中添加上FreeRTOS的相关文件,各文件的功能网上已经有很多介绍,就不啰嗦了,而且也不属于我们需要说明的内容。

添加完了之后,源码中就会添加创建一个缺省任务的代码。因为最少要有一个任务才有意义。查看main()函数,就会出现如下这样一段代码:

/* Create the thread(s) */

/* definition and creation of defaultTask */

osThreadDef(defaultTask, StartDefaultTask, osPriorityNormal, 0, 128);

defaultTaskHandle = osThreadCreate(osThread(defaultTask), NULL);

/* USER CODE BEGIN RTOS_THREADS */

/* add threads, ... */

/* USER CODE END RTOS_THREADS */

/* USER CODE BEGIN RTOS_QUEUES */

/* add queues, ... */

/* USER CODE END RTOS_QUEUES */

/* Start scheduler */

osKernelStart();

其实这段代码就实现两个功能:创建任务和情动任务调度。不过ST在FreeRTOS的基础上做了进一步的封装。其中osKernelStart()函数用于启动任务调度,这个函数比较简单只不过是调用了一下FreeRTOS中的任务调度函数vTaskStartScheduler(),其实现原型如下:

osStatus osKernelStart (void)

{

vTaskStartScheduler();

return osOK;

}

而实现任务创建的就是osThreadDef(defaultTask, StartDefaultTask, osPriorityNormal, 0, 128);和defaultTaskHandle = osThreadCreate(osThread(defaultTask), NULL);这两行代码。这两行代码就是创建了一个任务显示名称为defaultTask的StartDefaultTask任务。带是代码看起来有点不容易理解,其实是ST进行了进一步封装的结果。

首先osThreadDef(defaultTask, StartDefaultTask, osPriorityNormal, 0, 128)其实是一个宏,其定义的原型如下:

#define osThreadDef(name, thread, priority, instances, stacksz)  \

const osThreadDef_t os_thread_def_##name = \

{ #name, (thread), (priority), (instances), (stacksz)  }

那么osThreadDef_t又是个什么呢?其实是ST定义的创建任务的参数结构体:

typedef struct os_thread_def  {

char                   *name;        ///< Thread name

os_pthread             pthread;      ///< start address of thread function

osPriority             tpriority;    ///< initial thread priority

uint32_t               instances;    ///< maximum number of instances of that thread function

uint32_t               stacksize;    ///< stack size requirements in bytes; 0 is default stack size

} osThreadDef_t;

所以第一句的含义就很明确了,就是定义了一个osThreadDef_t类型的变量用于存储用于创建任务的参数。第二句很明显是一个函数调用,但参数与我们见到的FreeRTOS中的创建任务的函数有些不同。其实它的第一个参数osThread(defaultTask)也是一宏:

#define osThread(name)  \

&os_thread_def_##name

其实就是调用第一句中定义的osThreadDef_t类型的结构体变量。这样就明确了,第二句实际上就是调用FreeRTOS中的任务创建函数创建任务:

osThreadId osThreadCreate (const osThreadDef_t *thread_def, void *argument)

{

TaskHandle_t handle;

if (xTaskCreate((TaskFunction_t)thread_def->pthread,(const portCHAR *)thread_def->name,

thread_def->stacksize, argument, makeFreeRtosPriority(thread_def->tpriority),

&handle) != pdPASS)  {

return NULL;

}

return handle;

}

所以我们要创建其他的任务,只需照此设计即可。当然任务中的内容根据需要修改,STM32CubeMX添加的缺省任务,参数和实现内容也可以更改。在这里我们不对缺省任务做太多修改,在其任务函数中添加部分我们的内容。

考虑到我们所应用到的功能,我们需要6个任务,利用上缺省任务我们还需要添加5个任务。之所以要添加这么多的任务是因为我们不想让各种功能相互影响。在某一外设出现操作错误时不会影响其他部分的应用,这也是我们使用多任务实时操作系统的原因之一。

缺省任务我们让它来跑逻辑控制。再增加几个任务分别来跑各种功能,由于AD和DA都是通过SPI1通讯来完成的,所以采用同一个任务。

首先定义几个任务操作句柄:

/*定义任务句柄*/

osThreadId defaultTaskHandle;

osThreadId addaTaskHandle;

osThreadId ndirTaskHandle;

osThreadId lcdTaskHandle;

osThreadId ethernetTaskHandle;

osThreadId paraTaskHandle;

在声明对应的任务处理函数:

/*声明任务处理函数*/

void StartDefaultTask(void const * argument);

void ADDATask(void const * argument);

void NDIRTask(void const * argument);

void LCDTask(void const * argument);

void EthernetTask(void const * argument);

void ParameterTask(void const * argument);

编写各任务处理函数,由于各种功能在之前已经实现了所以任务函数比较简单:

/* 缺省任务,用来处理逻辑控制 */

void StartDefaultTask(void const * argument)

{

for(;;)

{

/*逻辑处理*/

LogicOperation();

osDelay(1);

}

}

/* AD/DC数据处理任务函数 */

void ADDATask(void const * argument)

{

for(;;)

{

/*获取AD采集的测量值*/

GetMeasuredValue();

/*设置模拟量输出*/

SetOutputValue();

osDelay(1);

}

}

/* 远红外炭氢检测数据处理任务函数 */

void NDIRTask(void const * argument)

{

for(;;)

{

/*获取CH4测量值*/

GetNDIRData();

osDelay(1);

}

}

/* LCD显示通讯处理任务函数 */

void LCDTask(void const * argument)

{

for(;;)

{

/*显示屏数据通讯*/

LCD_DataExchange();

osDelay(1);

}

}

/* 以太网通讯处理任务函数 */

void EthernetTask(void const * argument)

{

for(;;)

{

/*以太网通讯处理*/

EthernetProcess();

osDelay(1);

}

}

/* 参数存取处理任务函数 */

void ParameterTask(void const * argument)

{

for(;;)

{

/*参数的存储、恢复等处理*/

ParameterProcess();

osDelay(1);

}

}

再在系统中创建任务:

/* Create the thread(s) */

/* definition and creation of defaultTask */

osThreadDef(defaultTask, StartDefaultTask, osPriorityNormal, 0, 128);

defaultTaskHandle = osThreadCreate(osThread(defaultTask), NULL);

/* USER CODE BEGIN RTOS_THREADS */

osThreadDef(addaTask, ADDATask, osPriorityNormal, 0, 128);

addaTaskHandle = osThreadCreate(osThread(addaTask), NULL);

osThreadDef(ndirTask, NDIRTask, osPriorityNormal, 0, 128);

ndirTaskHandle = osThreadCreate(osThread(ndirTask), NULL);

osThreadDef(lcdTask, LCDTask, osPriorityNormal, 0, 128);

lcdTaskHandle = osThreadCreate(osThread(lcdTask), NULL);

osThreadDef(ethernetTask, EthernetTask, osPriorityNormal, 0, 128);

ethernetTaskHandle = osThreadCreate(osThread(ethernetTask), NULL);

osThreadDef(paraTask, ParameterTask, osPriorityNormal, 0, 128);

paraTaskHandle = osThreadCreate(osThread(paraTask), NULL);

/* USER CODE END RTOS_THREADS */

至此在系统中添加FreeRTOS的过程完成,编译调试看看结果:

编译无错误,运行正常。通过IO中断看看运行结果与没有操作系统时有没有区别:

在运行一段时间后再看看结果:

系统运行无误,与我们所希望的一样。

STM32F412应用开发笔记之九:移植FreeRTOS到F412ZG平台的更多相关文章

  1. FFmpeg开发笔记(九):ffmpeg解码rtsp流并使用SDL同步播放

    前言   ffmpeg播放rtsp网络流和摄像头流.   Demo   使用ffmpeg播放局域网rtsp1080p海康摄像头:延迟0.2s,存在马赛克     使用ffmpeg播放网络rtsp文件流 ...

  2. Java开发笔记(九十一)IO流处理简单的数据压缩

    前面介绍的文件I/O,不管是写入文本还是写入对象,文件中的数据基本是原来的模样,用记事本之类的文本编辑软件都能浏览个大概.这么存储数据,要说方便确实方便,只是不够经济划算,原因有二:其一,写入的数据可 ...

  3. Java开发笔记(九十七)利用Runnable启动线程

    前面介绍了线程的基本用法,按理说足够一般的场合使用了,只是每次开辟新线程,都得单独定义专门的线程类,着实开销不小.注意到新线程内部真正需要开发者重写的仅有run方法,其实就是一段代码块,分线程启动之后 ...

  4. STM32F412应用开发笔记之五:结合W5500实现以太网通讯

    因实际使用需求我们测试一下网络通讯,在NUCLEO-F412ZG测试板上没有以太网部分,我们选择外接一个W5500的实验板.W5500支持SPI接口通讯,DC3.3V供源.而NUCLEO-F412ZG ...

  5. STM32F412应用开发笔记之一:初识NUCLEO-F412ZG

    今天终于收到了期待已久的NUCLEO-F412ZG,感谢电子发烧友论坛! 近几年来基本都是在STM32平台上做一些设计开发工作.STM32F103.STM32F107.STM32F429等都应用过,但 ...

  6. STM32F412应用开发笔记之三:SPI总线通讯与AD采集

    本次我们在NUCLEO-F412ZG试验模拟量输入采集.我们的模拟量输入采用ADI公司的AD7705,是一片16位两路差分输入的AD采集芯片.具有SPI接口,我们将采用SPI接口与AD7705通讯.两 ...

  7. STM32F412应用开发笔记之二:基本GPIO控制

    NUCLEO-F412ZG板子上的元器件并没有完全焊接,除去ST-LINK部分和电源部分后,还有用一个USB主机接口,三个LED灯和两个按钮,不过很多功能引脚都已经引到了插针.查看原理图可发现,由原理 ...

  8. Modbus库开发笔记之九:利用协议栈开发Modbus TCP Server应用

    前面我们已经完成了Modbus协议栈的开发,但这不是我们的目的.我们开发它的目的当然是要使用它来解决我们的实际问题.接下来我们就使用刚开发的Modbus协议栈开发一个Modbus TCP Server ...

  9. STM32F412应用开发笔记之十:多组分气体分析仪设计验证

    本次将NUCLEO-F412ZG应用于我们的多组分气体分析仪的实现试验,从整体上测试实际项目的应用情况. 一.项目概述 多组分气体分析仪是我公司近期研发的三个主要产品之一.采用模块化设计,可增减配置, ...

随机推荐

  1. P4147 玉蟾宫

    P4147 玉蟾宫 给定一个 \(N * M\) 的矩阵 求最大的全为 \(F\) 的子矩阵 Solution 悬线法 限制条件为转移来的和现在的都为 \(F\) Code #include<i ...

  2. kafka channle的应用案例

      kafka channle的应用案例 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 最近在新公司负责大数据平台的建设,平台搭建完毕后,需要将云平台(我们公司使用的Ucloud的 ...

  3. parted分区工具用法

    parted分区工具用法 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 随着生产环境中数据量的增大,我们对硬盘的容量也有很大的需求,当硬盘的容量大于2T(工业上的最大磁盘2.2TB ...

  4. Linux上安装Perl模块的两种方法

    Linux/Unix下安装Perl模块有两种方法:手工安装和自动安装.第一种方法是从CPAN上下载  您需要的模块,手工编译.安装.第二种方法是联上internet,使用一个叫做CPAN的模块自动完 ...

  5. .NET Framework自带的文件内存映射类

    最近一直为文件内存映射发愁,整个两周一直折腾这个东西.在64位系统和32位系统还要针对内存的高低位进行计算.好麻烦..还是没搞定 偶然从MSDN上发现.NET 4.0把内存文件映射加到了.NET类库中 ...

  6. TCP和UDP的区别和优缺点

    1.TCP与UDP区别总结: 1.TCP面向连接(如打电话要先拨号建立连接);UDP是无连接的,即发送数据之前不需要建立连接2.TCP提供可靠的服务.也就是说,通过TCP连接传送的数据,无差错,不丢失 ...

  7. Java编程思想 学习笔记5

    五.初始化与清理 1.用构造器确保初始化  在Java中,通过提供构造器,类的设计者可确保每个对象都会得到初始化.创建对象时,如果其类具有构造器,Java就会在用户有能力操作对象之前自动调用相应的构造 ...

  8. Ubuntu14.10安装TensorFlow1.0.1

    本文记录了在Ubuntu上安装TensorFlow的步骤.系统环境:Ubuntu14.10 64bitPython版本:Python 2.7.8TensorFlow版:TensorFlow 1.0.1 ...

  9. Mac下MySQL与MySQLWorkbench的安装

    通过查阅各种各样的资料,去安装这些东东.最后经过一番周折终于安装完成.下面是对安装过程和遇到的问题做个简单记录. 一. 下载MySQL和MySQL Workbench http://dev.mysql ...

  10. FastReport"Text"对象中的HTML标签介绍以及使用

    "Text"对象可以理解一些简单的HTML标签.标签可位于对象中的文本.在默认情况下,标签是禁用的,要启用这些HTML标签,可选择对象上下文菜单中的“Allow HTML tags ...