stm8s_atomthread

2020-01-16来源: eefocus关键字:stm8s  atomthread

STM8S Atomthread 实时操作系统移植

介绍


1.嵌入式操作系统基本知识

嵌入性、专用性与计算机系统是嵌入式系统的基本元素;跟通用计算机系统(如windows、linux等)相比,嵌入式系统具备专用性强、可剪裁性好、实时性好和功耗低的特点。

实时操作系统满足条件: 

必须是多任务(任务调度或调度器,最核心功能)

任务的切换时间与系统当前任务数无关(调度器对任务切换时间)

中断延时的时间可预知并尽可能短(任务实时性要求,即为CPU对任务响应速度)

目前,实时系统主要类型: 

抢占式(剥夺式)

非抢占式(非剥夺式)


2.实时操作系统介绍:Atomthread

Atomthread完全开源、轻量、便捷,针对于嵌入式操作系统的实时调度。

具备特性:抢占式、无限的线程(在RAM允许的条件下)、255个优先级、相同优先级的时间片轮、任务同步与互斥、队列、定时器、具备元素的阻塞与非阻塞、线程的堆栈分析等系统基本元素。

对于初步学习操作系统的非计算机类学生来说是简便的,尤其是电子类学生或从事嵌入式设计的人是有效的系统学习对象。官方原生支持STM8系列CPU,配套电协第5代开发板,相应编写本教程,有助于大家进一步理解单片机系统。

系统文件组成:

文件名 文件作用 备注

atom.h 系统API 任务控制块、系统错误宏、系统API等

atomkernel.c 系统内核源码 内核功能:上下文切换、中断、TCB控制、信号量等

atommutex.c 互斥源码 **

atomport-template.h 系统宏、类型声明 时间片轮设定

atomqueue.c 队列源码 **

atomsem.c 信号量源码 **

atomtimer.c 定时器源码 系统滴答时钟、应用性定时器


3.注意:

Atomthread官方网站:http://atomthreads.com/

以下编写当中,涉及到的变量、宏及函数等,通过()提供文件位置,请注意。

Atomthread(STM8版本)使用指南


1.STM8S版本特点

移植唯一需要修改的汇编文件。

编译器的特点: IAR编译器的虚拟寄存器、变量空间选择关键字等特点。

Atom官方原生支持使用ST公司的官方库文件,应用代码都将使用库函数而不再是直接操作寄存器。学习需要注意数据结构体的设计和指针的灵活使用。


2.使用Atom基本流程:

系统初始化:设置空闲堆栈大小

启动系统时钟 

根据实际调节系统时间片轮

根据实际调节滴答定时器

创建线程 

设置任务堆栈大小

创建任务区(任务函数)

启动系统


3.电协

电协为广大的学习者,将ST库版本从1.1修改为V2.1(目前最新版本),库函数更完整。同时独自移植了一套直接操作寄存器的Atom版本,提供多种选择。本文,基于库函数版本编写。

实验平台: 

IAR V6.3

电协第五代开发板-B版

软件工具:source insight 3.5

电协修改后的源代码文件结构: 

kernel:Atom系统内核

ports:特定平台(根据不同的芯片选择) 

lib:库函数 

inc:库函数头文件

src:库函数源文件

ports-stm8s:主程序和系统的端口代码,涵盖编译器的选择

project:工程文件

tests:Atom官方测试代码

user:应用程序代码

学习建议:凡是讲述代码部分,建议正确打开工程,一步步跟着讲述,查看每一个提及到的变量、函数等,这样对系统加深理解。我们是学习实时系统的原理,而不单是使用它,而且要有信心优化系统。


原理:Atomthread系统的整体架构解析

1.程序控制块(atom.h)

操作系统是一种管理软件,负责管理对象的信息之余还按照某种规则对这些对象进行分配、调度,而实现这些操作的前提是需要有一份关于对象的详细信息,我们称之为程序控制块。从代码上看,程序控制块就是一个结构体。在Atom系统里面,程序控制块的组成简化如下图。 

这里写图片描述 

Atom系统里把上图所示结构体称之为ATOM_TCB(atom.h 4行),系统依赖于此结构来执行系统任务程序。 


*注意:任务堆栈使用记录,是由ATOM_STACK_CHECKING宏决定是否存在TCB当中,如果不需要使用堆栈检测功能,可以根据宏来设置。


2.任务(kernel.h)

生活中,我们习惯将大而复杂的事情“分而治之”,程序也会遇到类似的问题;也有时候,我们需要等待用户按下按键,同时也需要不断读取来自网络的数据。可见,多任务的好处就是用来解决并发性问题。一个问题,我们称之为任务,程序需要通过算法去将这个任务执行并解决。而多线程便解决多任务问题,所以,操作系统正是通过系统规则对任务进行分配资源、调度处理。Atom系统的任务及其内存结构如下图: 

这里写图片描述 

由此可得,如果多个任务,就存在一张链表保存着所有任务的信息,通过图上标号F链接前一个任务控制块指针,标号N链接后一个任务控制块指针。系统则可以通过任务控制块链表来找到任意一个存在的任务,从而执行多个任务的程序。 


Atom系统将此链表称为tcbReadyQ(kernel.h 172行) 

至此,系统运行的“地基”已经造好。


3.(系统任务)系统程序(kernel.h)

系统初始化函数:atomOSInit()(kernel.h 657行)


任务链表 tcbReadyQ

系统的当前任务指针 curr_tcb

系统开关变量 atomOSStarted

创建空闲任务 idle_tcb(堆栈设置、优先级)

编写的代码中,除了有用户任务(处理应用程序逻辑)还需要有系统任务(处理系统逻辑),我们需要给予系统足够的堆栈空间运行,才能处理系统任务。Atom系统初始化系统自身的同时,也为自己创建了一个名为“空闲任务”的线程。 截取该函数主要代码解析:


    /* 初始化系统全局变量 */

    curr_tcb = NULL;//系统运行期间,该指针一直指向正在执行的任务控制块

    tcbReadyQ = NULL;//系统运行期间,该指针指向了保存所有任务控制块信息的链表

    atomOSStarted = FALSE;//作为系统的总开关变量,FALSE为停止系统调度任务,

                          //TRUE为开启系统调度任务


    /* 创建空闲线程(idle thread) */

    status = atomThreadCreate(&idle_tcb,//系统任务:空闲线程的程序控制块

                 IDLE_THREAD_PRIORITY,//空闲线程优先级,最低优先级255

                 atomIdleThread,//任务函数,空闲程序入口,不作用户任务。

                 0,

                 idle_thread_stack_top,//堆栈顶

                 idle_thread_stack_size);//堆栈大小


读者亲自查看代码,容易发现初始化系统函数仅接受两个参数(stack_top和stack_size),这意味着我们初始化系统只需要设置这连个参数,而函数内其他都是固定设置的,包括创建的idle_tcb。可见,系统启动必须存在“空闲任务”线程,它在系统没有其他用户任务需要处理的时候执行。当然,它只是一个死循环。


4.时间片轮(atomport.h)

时间片轮初始化函数:archInitSystemTickTimer()(atomport.c 237行),该函数启动STM8内部时钟(任意一个),并设置合适的定时间隔作为系统的心跳时间。系统的心跳快慢决定着系统切换任务的速率,所以需要根据实际来调节定时间隔。编写该函数等同于以往初始化某一定时器,详情翻查定时器章节。


每秒系统心跳次数:SYSTEM_TICKS_PER_SEC(atomport.c 237行),一个宏保存了系统心跳速率,方便使用其余系统函数。假设,定时间隔为10ms,那么该宏的值为100,合计1S。

时间片轮中断服务函数:System tick ISR函数名字根据实际决定,电协Atom版本于stm8s_it.c 225行,编写TIM1_UPD中断函数。

  /* 中断入口函数,中断嵌套层数全局变量atomIntCnt加一 */

  atomIntEnter();


  /* 系统时钟处理函数,心跳次数加一,并启动定时器回调 */

  atomTimerTick();


  /* 清空标志位 */

  TIM1->SR1 = (uint8_t)(~(uint8_t)TIM1_IT_UPDATE);


  /* 中断退出函数, atomIntCnt减一,调用系统调度程序*/

  atomIntExit(TRUE);


该程序,完成了系统自动调度任务功能,至此实时系统最基本的调度功能完成。


5.(用户任务)线程创建(kernel.h)

线程创建函数:atomThreadCreate() 

- 创建一个新线程并根据函数输入参数设置初始化TCB参数(挂起状态、优先级、队列指针、任务入口、入口参数) 

- 调用archThreadContextInit(),入栈操作。将thread_shell()返回信息记录在线程的TCB主中,使得下次调度后执行正确的线程开始位置。 


- 该函数内调用thread_shell()函数,能够找到线程处理函数入口地址。 

- 将该线程放到任务队列tcbReadyQ 

- 调用系统调度函数根据优先级决定线程运行,如果系统没有启动,则不进行调度。


线程堆栈问题: 

1. 线程被创建,随后由系统调度挂起或执行,都需要足够的堆栈区,用以保存线程的资源。任务的大小决定着线程堆栈大小,实际调试根据任务的复杂度来设置堆栈大小。 

2. Atom为线程提供堆栈检测接口,除了直观地设置堆栈大小,在声明了ATOM_STACK_CHECKING的情况下,还可以调用atomThreadStackCheck()函数检测线程堆栈使用情况。


6.任务调度(kernel.h)

系统任务调度函数:atomSched(),下面提供函数

[1] [2]
关键字:stm8s  atomthread 编辑:什么鱼 引用地址:http://news.eeworld.com.cn/mcu/ic485844.html 本网站转载的所有的文章、图片、音频视频文件等资料的版权归版权所有人所有,本站采用的非本站原创文章及图片等内容无法一一联系确认版权者。如果本网所选内容的文章作者及编辑认为其作品不宜公开自由传播,或不应无偿使用,请及时通过电子邮件或电话通知我们,以迅速采取适当措施,避免给双方造成不必要的经济损失。

上一篇:stm8使用atomthreads项目
下一篇:stm8L 触摸库使用教程

关注eeworld公众号 快捷获取更多信息
关注eeworld公众号
快捷获取更多信息
关注eeworld服务号 享受更多官方福利
关注eeworld服务号
享受更多官方福利

推荐阅读

stm8s输入捕获
重装载的值,49ms中断    TIM1_CNTRH = 0xFF;    TIM1_CNTRL = 0xFF;       TIM1_CCER1_CC1E = 0;//禁止捕获使能    TIM1_CCMR1_CC1S = 1;//输入捕捉//采样频率f SAMPLING =f MASTER ,N=8 //捕获输入口上检测到的每个事件触发一次捕获    TIM1_CCER1_CC1P = 1;//1:捕捉发生在TI1F的低电平或下降沿     0:捕捉发生在TI1F的高电平或上升沿
发表于 2020-01-09
stm8s 实践课程之IAP设计编码(bootloader实现)
模式Normal Mode:这个模式主要作用为检测主程序是否有效,如果有效则进行程序跳转进入主程序,否则进行下一个模式的检测。这边建议在主程序控制LED指示灯以其他的频率(例如500ms或者1s)闪烁,以示区别。3.等待模式Wait Mode:如果上述两种模式都不满足,则停留在这个模式。这个模式主要作用为循环检测串口数据,如果有收到数据则进行解析,满足升级模式的进入条件则转到升级模式。该模式下,LED指示灯闪烁速率为200ms。程序流程框图如下:2.1.正常模式从之前的程序流程图可以看出,我们已经将stm8s的flash分为了Bootloader和Main APP,所以如果没有接到烧录指令且Main APP的flash区域已经有了正常的
发表于 2020-01-07
stm8s 实践课程之IAP设计编码(bootloader实现)
STM8s串口2异步基本收发使用说明
学到定时器部分时,想要将调试信息输出,故先把串口基本功能学了,才能方便后面的调试。使用某宝上买的STM8S最小系统板,外部晶振为8MHz的。HSI的误差对串口波特率影响比较大,这里使用外部晶振。开发板芯片是105K4的,只有UART2,这里把UART2的整体框图放在下面,蓝色矩形框是串口异步基本收发所用到的寄存器,可以看到,所要用到的寄存器很少。这里先说下如何计算波特率以及误差多少:官方手册已经给出:这里我使用的波特率为9600bps,然后主时钟为8M,这样分频因子为8M/9600约等于833=0x341,计算的波特率为8M/833=9603.8,误差为(9603.8-9600)/9600*100%约等于0.04%。如何配置串口
发表于 2020-01-07
STM8s串口2异步基本收发使用说明
关于STM8S的UART2串口的学习记录以及使用经验
前言这是本人第一次接触STM8S单片机,所以记录一下,方便日后查找。正文Uart2串口作为STM8S的标准串口之一,它的作用就是以有线连接的方式为STM8S单片机提供对外通讯的通道。初始化要想使用串口的第一步就是要完成对串口相关引脚的初始化以及串口本身的初始化。具体代码如下:GPIO_DeInit(GPIOD);GPIO_Init(GPIOD, GPIO_PIN_5, GPIO_MODE_IN_PU_NO_IT);GPIO_Init(GPIOD, GPIO_PIN_6, GPIO_MODE_IN_FL_NO_IT);   UART2_DeInit();UART2_Init(115200
发表于 2020-01-07
STM8学习笔记---读取STM8S003单片机序列号
最近做项目时需要用到单片机的序列号,于是查了一下STM8S003单片机UID读取的方法。读取UID方法大概分两种,一种是直接在地址中读取,一种是通过定义一个联合体,将联合体的起始地址设置在UID起始地址处,在初始化的时候直接将UID号存入联合体中。关于UID的描述,官方资料如下:STM8S003F3芯片Unique ID的首地址为0X4865,连续读取后十二字节数据即为STM8S003F3芯片Unique ID。有些STM8S系列的芯片Unique ID首地址不是0X4865,STM8S105系列芯片Unique ID首地址为0X48CD。接下来看一下UID的读取方法:一:直接从UID收地址开始读取12个字节#define
发表于 2020-01-02
STM8S003单片机串口通信通信协议分析
最近在用STM8S003这个片子做项目,在做串口通信的时候,发现以前写的协议太简单了,项目中用不适合。//协议 : 0XDD xx xx  xx xx xx xx  0XAA  @far @interrupt void UART1_Receive(void) {unsigned char res;res=UART1_DR;if(res==0xDD)       //头{Rec_statu=1;     //标志开始接收Rec_Cnt=0;Rec_End=0;return;}if(res==0xAA) 
发表于 2019-12-27
小广播
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

电子工程世界版权所有 京ICP证060456号 京ICP备10001474号 电信业务审批[2006]字第258号函 京公海网安备110108001534 Copyright © 2005-2020 EEWORLD.com.cn, Inc. All rights reserved