创建线程Creating Threads

一旦RTOS开始运行,就会有很多系统调用来管理和控制活跃的线程。默认情况下,main()函数自动被创建为第一个可运行的线程。在第一个例子里我们使用main()函数创建了其他线程,并且随后让main()结束运行。然而我们还可以让main当成一个真正的thread使用。首先,我们需要获取它的ID号。此时,我们第一个要调用的RTOS函数就是osThreadGetId(),这个函数返回当前运行thread的ID号,并把它存在ID句柄里。当我们未来某一时刻在OS调用中需要这个线程时,我们使用的就是这个线程的句柄,而非线程的函数名。

  1.  osThreadId main_id;//创建线程句柄
  2.  void main(void)
  3.  {
  4.  /*获取main线程的ID号*/
  5.  main_id = osThreadGetId();
  6.  while(1)
  7.  {}
  8.  }

现在我们有了main的ID句柄,就可以创建其他应用线程,并随后调用osTermainate(main_id)来结束main线程。相比让一个线程运行到最后一个花括号来结束,这种结束方法更好一些。当然,我们还可以添加while(1)这个循环来继续使用main函数。

在第一个例子中我们就看到main线程可以作为一个launcher 线程来创建其他应用线程。仅需要两步就行:第一步,定义线程结构体(同时还可以定义线程传递参数)。

  1.  osThreadId thread_id; //线程句柄
  2.  void thread1(void const *argument); //thread1的函数原型
  3.  osThreadDef(thread1, osPriorityNormal, 1, 0); //线程定义结构体

定义线程结构体需要先定义线程函数名、线程优先级、创建线程的实例化个数和它的栈大小。这些参数后续会详细说明。一旦定义了线程结构体,我们就可以用osThreadCreated() API来创建线程。线程经常在main线程里创建,当然也可以在其他地方。

thread1_id = osThreadCreated(osThread(thread1), NULL);

上面这条代码创建线程并启动它运行。另外,在启动线程时可以给它传递个参数。

  1.  uint32_t startupParameter = 0x23;
  2.  thread1_id = osThreadCreate(osThread(thread1), startupParameter);

当创建线程时,还将给它分配一个栈,用来存储上下文切换的数据。注意不要和Cortex处理器栈混淆,它只是分配给线程的一段存储空间。在RTOS配置文件里已经定义了一个默认的栈大小,当然我们也可以自定义栈的大小。osThreadDef()这个函数的最后一个参数设为0时表示使用默认栈大小。如果需要的话,也可以通过在线程结构体里定义一个更大的栈来增加额外的存储资源。

  1. osThreadDef(thread1, osPriorityNomal, 1, 0); //分配默认栈大小 
  2. osThreadDef(thread2, osPriorityNomal, 1, 1024); //分配1kB的栈大小

当然,如果你分配了更大的栈空间,在RTOS配置文件里就需要增加额外的存储空间。

线程的管理和优先级Thread Management and Priority

在创建线程的时候就会给它分配一个优先级。RTOS的调度器靠线程的优先级来决定要调度哪个线程运行。假如同时有几个线程都准备运行,优先级最高的线程就会进入运行状态。如果一个高优先级线程处于准备运行状态,它就会抢占正在运行的低优先级线程。有一点非常重要,高优先级线程占用CPU运行的时候永远不会停止,除非有RTOS API调用来阻塞它,或者有更高优先级的线程来抢占它。线程的优先级在线程结构体中定义,下面列出可用的优先级定义,其中一般默认的优先级是osPriorityNormal。

CMSIS-RTOS Priority Levels
osPriorityIdle
osPriorityLow
osPriorityBelowNormal
osPriorityNormal
osPriorityAboveNormal
osPriorityHigh
osPriorityRealTime
osPriorityError

一旦线程开始运行,我们就可以使用几个OS系统调用来管理线程。当然也可以通过其他函数或在它自己代码里面提升或者降低它的优先级。

osStatus osThreadSetPriority(threadID, priority);
osPriority osThreadGetPriority(threadID);

在创建线程之后,也可以通过它自己或者其他活跃的线程来删除它。再强调一次,我们使用thread ID而不是它的函数名来操作。

osStatus = osThreadTermainate(threadID1);

另外,同优先级线程间的切换有一种特殊情况,就是使用协作(co-operative)切换方式:

osStatus osThreadYield();//切换到另外一个准备运行的线程

多重实例化Multiple Instances

RTOS的一个有趣功能就是可以针对一个线程进行多个实例化,例如你可以基于一个线程代码创建多个用于控制UART的实例,此时,每个UART的实例都会管理一个不同的UART硬件。

首先我们要创建线程结构体,并设置线程实例个数为2:

osThreadDef(thread1, osPriorityNormal, 2, 0);

空闲线程Idle Demon

CMSIS-RTOS提供的最后一个定时器服务函数并不是一个真正的定时器,但是这里是最合适讨论它的地方。如果在我们的RTOS程序里没有任何线程正在运行,或者准备运行(举个例子,所有的线程都处于等待延时函数中),那么RTOS就会利用空闲的运行时间调用一个“Idle Demon”的线程,这个函数同样位于RTX_Conf_CM.c文件里面,空闲线程拥有一个低优先级,只有在没有其他任何线程准备运行的情况下才会运行。

void os_idle_demon(void)
{
for(;;){
/*此处包含一些用户的的可选代码,在没有线程运行时执行*/
}
}

你可以在这个线程里加入任何代码,但是同样要遵守常规线程的基本准则,一个最简单的应用就是在此线程中添加控制器MCU的低功耗模式。

void os_idle_demon(void)
{
__wfe();
}

下一步什么情况取决于MCU的功耗模式选择,至少CPU会暂停,直到sysTick产生中断并继续执行调度器。如果有线程准备运行,CPU就会去执行这个线程,否则,就会重新进入空闲线程,系统也会重新进入睡眠状态。

线程间通信 Inter-Thread Communication

前面我们已经学习了如何把你的应用代码设计成独立的线程,以及如何访问RTOS的时间服务函数。在实际的工程应用中,线程间的通信是必不可少的,任何一个RTOS都会支持几种通信方式来连接各种不同的线程。CMSIS-RTOS API支持的通信方式有:信号(signals),信号量(semaphores),互斥锁(mutexes),邮箱(mailboxes)和消息队列(message queues)。所有这些首要的核心概念就是并发性。在这一章,将集中讨论多任务间的同步问题。

信号 Signals

CMSIS RTOS RTX支持单线程16种信号标志,这些信号存储在线程控制块里,一个线程会暂停执行,直到一个或一组信号标志被其他线程置位。

每个线程有16个信号标志,一个线程可能被置于等待状态,直到一种模式的信号被其他线程置位。然后此线程就会返回等待状态,并等待调度器调度到运行状态。

调用信号等待后,系统会挂起正在运行的线程,并把它置于等待事件(wait_event)状态,直到所有的信号标志被置位,线程才会恢复运行。当然也可以加入超时机制,以便让线程可以顺利返回等待状态。默认初始化超时值是0xFFFF。

osEvent osSignalWait(int32_t signals, uint32_t millisec);

调用osSignalWait之后会把信号复位,如果后面还有信号被置位,线程就可以继续执行,你可以读osEvent.value.signals的值来获取哪个标志被置位。

任何一个线程都可以置位或清除另外一个线程的信号:

int32_t osSinalSet(osThreadId thread_id, int32_t signals);
int32_t osSignalClear(osThreadId thread_id, int32_t signals);

来源:https://blog.csdn.net/ichamber/article/details/53116253

ARM官方《CMSIS-RTOS教程》之线程Threads的更多相关文章

  1. 【转帖】ECLIPSE-JEE-LUNA-SR2官方汉化教程

    ECLIPSE-JEE-LUNA-SR2官方汉化教程 工具/原料 Eclipse-jee-luna-SR2 步骤/方法 1.在浏览器输入网址http://www.eclipse.org/babel/d ...

  2. ActiveReports 报表控件官方中文入门教程 (3)-如何选择页面报表和区域报表

    本篇文章将介绍区域报表和页面报表的常见使用场景.区别和选择报表类型的一些建议,两种报表的模板设计.数据源(设计时和运行时)设置.和浏览报表的区别. ActiveReports 报表控件官方中文入门教程 ...

  3. ActiveReports 报表控件官方中文入门教程 (1)-安装、激活以及产品资源

    本系列文章主要是面向初次接触 ActiveReports 产品的用户,可以帮助您在三天之内轻松的掌握ActiveReports控件的基本使用方法,包括安装.激活.创建报表.绑定数据源以及发布等内容.本 ...

  4. ActiveReports 报表控件官方中文入门教程 (2)-创建、数据源、浏览以及发布

    本篇文章将阐述首次使用 ActiveReports 报表控件 的方法,包括添加报表文件.绑定数据源以及如何发布报表等内容. ActiveReports 报表控件官方中文入门教程 (1)-安装.激活以及 ...

  5. ARM开发软件ADS教程

    ARM开发软件ADS教程 ADS(ARM Developer Suite)是ARM公司推出ARM集成开发环境,操作简单方便,获得广大开发人员的青睐.下面使用ADS v1.2做一个实例教程,帮助大家学会 ...

  6. Spring Boot 与 OAuth2 官方最详细教程

    https://mp.weixin.qq.com/s?__biz=MzU0MDEwMjgwNA==&mid=2247484357&idx=1&sn=73e501de8591e6 ...

  7. ActiveReports 报表控件官方中文新手教程 (1)-安装、激活以及产品资源

     本系列文章主要是面向初次接触 ActiveReports 产品的用户,能够帮助您在三天之内轻松的掌握ActiveReports控件的基本用法,包含安装.激活.创建报表.绑定数据源以及公布等内容. ...

  8. 微信小程序导航:官方工具+精品教程+DEMO集合(1月7更新)

    1:官方工具:https://mp.weixin.qq.com/debug/w ... tml?t=14764346784612:简易教程:https://mp.weixin.qq.com/debug ...

  9. CMSIS RTOS -- embOS segger

    #ifndef __CMSIS_OS_H__ #define __CMSIS_OS_H__ #include <stdint.h> #include <stddef.h> #i ...

随机推荐

  1. LastIndexOf干什么用的

    LastIndexOf的作用是对字符串进行从后往前的检索,找到第一个匹配的位置.比如对字符串“abcdbcd”执行lastindexof("bc")操作,得到的结果是4:4是从前往 ...

  2. swift中高阶函数map、flatMap、filter、reduce

    Swift相比于Objective-C又一个重要的优点,它对函数式编程提供了很好的支持,Swift提供了map.filter.reduce这三个高阶函数作为对容器的支持. 1 map:可以对数组中的每 ...

  3. 脚本_实时显示网卡eth0上的数据流量

    #!bin/bash#功能:使用死循环,实时显示网卡eth0发送的数据包流量#作者:liusingbonwhile : do       echo "本地网卡eth0的数据流量信息如下:&q ...

  4. Python3基础笔记---线程与进程

    参考博客:Py西游攻关之多线程(threading模块) 一.并发与并行的区别 并发:交替做不同事的能力并行:同时做不同事的能力 行话解释:并发:不同代码块交替执行的性能并行:不同代码块同时执行的性能 ...

  5. Tarjan专题

    前排Orz tarjan tarjan算法在图的连通性方面有非常多的应用,dfn和low数组真是奥妙重重(并没有很搞懂反正背就完事了) 有向图强连通分量 #include<iostream> ...

  6. element-ui的table表格控件表头与内容列不对齐问题

    原文链接:点我 element-ui的table表格控件表头与内容列不对齐问题 解决方法:将以下样式代码添加到index.html.或app.vue中(必须是入口文件,起全局作用!)body .el- ...

  7. POJ-2253 Frogger dijsktra查找间隔最小的路径

    题目链接:https://cn.vjudge.net/problem/POJ-2253 题意 一只Forg需要从节点1走到节点n 现要找一条各个间隔最小的路径 问间隔最小是多少 思路 用dijsktr ...

  8. Laravel修炼:服务容器绑定与解析

    前言   老实说,第一次老大让我看laravel框架手册的那天早上,我是很绝望的,因为真的没接触过,对我这种渣渣来说,laravel的入门门槛确实有点高了,但还是得硬着头皮看下去(虽然到现在我还有很多 ...

  9. 紫书 习题 11-15 UVa 1668 (图论构造法)

    参考了http://www.bubuko.com/infodetail-1276416.html 首先是逆向思维, 向把每条边看作一条路径, 然后再去合并 然后我们讨论怎么样合并时最优的 我们讨论当前 ...

  10. 传纸条 NOIP2008 洛谷1006 二维dp

    二维dp 扯淡 一道比较基本的入门难度的二维dp,类似于那道方格取数,不过走过一次的点下次不能再走(看提交记录里面好像走过一次的加一次a[i][j]的也AC了,,),我记得当年那道方格取数死活听不懂, ...