uC/OS-III 内核实现与应用开发实战学习(一)
参考教程
[野火]《uCOS-III内核实现与应用开发实战指南》
开发板---->野火MINI_STM32F103RCT6
1.轮询系统
2.前后台系统
3.多任务系统
事件的响应和处理方式 | |
---|---|
轮询系统 | 在主程序响应和处理 |
前后台系统 | 中断响应,在主程序处理 |
多任务系统 | 中断响应,在任务中处理 |
堆栈的认识
我们所说的堆栈其实是两个东西,分别是堆和栈,当然如果有人给你说起堆栈这个名词,那他想要表达的一定是栈这个概念。除了放置全局变量是在堆区,其他像局部变量,函数参数全部分配在栈区,栈是由高地址向低地址生长的。这一点与堆不一样,堆由低地址向高地址生长。
分配方式:堆都是动态分配的,没有静态分配的堆。
cpu.h程序
/*cpu.h头文件内容*/
#ifndef CPU_H
#define CPU_Htypedef unsigned short CPU_INT16U;
typedef unsigned int CPU_INT32U;
typedef unsigned char CPU_INT08U;typedef CPU_INT32U CPU_ADDR;/*堆栈数据类型重定义*/typedef CPU_INT32U CPU_STK;
typedef CPU_ADDR CPU_STK_SIZE;
typedef volatile CPU_INT32U CPU_REG32;#endif
结构体能不能作为参数传入函数?
os_task.c程序
/*实现任务创建函数*/void OSTaskCreate(OS_TCB *p_tcb,OS_TASK_PTR p_task,void *p_arg,CPU_STK *p_stk_base,CPU_STK_SIZE stk_size,OS_ERR *p_err)
{CPU_STK *p_sp;p_sp = OSTaskStkInit( p_task, p_arg,p_stk_base,stk_size);p_tcb->StkPtr = p_sp;p_tcb->StkSize = stk_size;*p_err = OS_ERR_NONE;//执行到这一步,表示没有错误!}
/*(1)p_tcb是任务控制块TCB指针(2)p_task是任务函数名,类型为OS_TASK_PTR,原型声明在os.h typedef void (*OS_TASK_PTR)(void *p_arg);(3)p_arg是任务形参,用于传递任务参数(4)p_stk_base 用于指向任务堆栈的起始地址(5)stk_size 表示任务堆栈的大小(6)p_err用于存放错误码,这个错误码是uC/OS-III已经预定义好的,错误码是枚举类型
*//*错误码枚举类型*/typedef enum os_err {OS_ERR_NONE = 0u,OS_ERR_A = 10000u,OS_ERR_ACCEPT_ISR = 10001u,OS_ERR_B = 11000u,OS_ERR_C = 12000u,OS_ERR_CREATE_ISR = 12001u,/* 篇幅限制,中间部分删除,具体的可查看本章配套的例程 */OS_ERR_X = 33000u,OS_ERR_Y = 34000u,OS_ERR_YIELD_ISR = 34001u,OS_ERR_Z = 35000u
} OS_ERR;
-------------------------------写于2022.1.10 晚上12.25,明天再继续看
1.10 下午7.10 Hello!兄弟们,继续啃!
在OSTaskCreate()函数中我们调用了OSTaskStkInit()函数,我们来看看OSTaskStkInit()的函数原型:
CPU_STK *OSTaskStkInit(OS_TASK_PTR p_task,)void *p_arg,CPU_STK *p_stk_base,CPU_STK_SIZE stk_size)
{CPU_STK *p_stk;p_stk = &p_stk_base[stk_size];//异常发生时自动保存的寄存器*--p_stk = (CPU_STK)0x01000000u; //xPSR的bit24必须置1*--p_stk = (CPU_STK)p_task; //R15(PC)任务的入口地址*--p_stk = (CPU_STK)0x14141414u; //R14(LR) *--p_stk = (CPU_STK)0x12121212u; //R12*--p_stk = (CPU_STK)0x03030303u; //R3*--p_stk = (CPU_STK)0x02020202u; //R2*--p_stk = (CPU_STK)0x01010101u; //R1*--p_stk = (CPU_STK)p_arg; //R0:任务参数//异常发生时需手动保存的寄存器R4~R11*--p_stk = (CPU_STK)0x11111111u; //R11*--p_stk = (CPU_STK)0x10101010u; //R10*--p_stk = (CPU_STK)0x09090909u; //R9*--p_stk = (CPU_STK)0x08080808u; //R8*--p_stk = (CPU_STK)0x07070707u; //R7*--p_stk = (CPU_STK)0x06060606u; //R6*--p_stk = (CPU_STK)0x05050505u; //R5*--p_stk = (CPU_STK)0x04040404u; //R4return (p_stk);}
刚开始我发现寄存器号与16进制数”居然一样“,疑惑下,不会这么巧?看到后面发现处于调试方便,故意这样做的
给出程序的解释:
(1)p_task 是任务名,指示着任务的入口地址,在任务切换的时候,
需要加载到 R15,即 PC 寄存器,这样 CPU 就可以找到要运行的任务。
(2)p_arg 是任务的形参,用于传递参数,在任务切换的时候,需要
加载到寄存器 R0。R0 寄存器通常用来传递参数。
(3)p_stk_base 表示任务堆栈的起始地址。
(4)stk_size 表示任务堆栈的大小,数据类型为 CPU_STK_SIZE,在
Cortex-M3 内核的处理器中等于 4 个字节,即一个字。
typedef unsigned int CPU_INT32U;
typedef CPU_INT32U CPU_ADDR;
typedef CPU_ADDR CPU_STK_SIZE;
(5)获取栈顶地址,前面我们说过栈有个特殊的地方,就是由高地址向低地址生长,
参考博客:手撕startup_stm32f10x_hd.s启动文件源码
(6)任务第一次运行,加载到CPU寄存器的环境参数我们要预先初始化好。初始化的顺序固定(这个是为什么?我认为xPSR,PC,LR固定顺序就OK啊?其他的没必要啊)
异常发生时自动保存的8个寄存器,即xPSR,PC,LR,R12,R3,R2,R1,R0(这个为什么就是自动保存的?其他为什么又要手动的?)
xPSR寄存器的位24又为什么必须为1?
(7)手动加载的R11~R4CPU寄存器参数
(8)return返回栈顶指针,赋值给p_sp,然后p_sp保存在TCB结构体的第一个成员StkPtr。
stk_size任务堆栈的大小保存在TCB第二个成员StkSize
//在函数OSTaskStkInit中
return (p_stk);
//在函数OSTaskCreate中
p_sp = OSTaskStkInit( p_task, p_arg, p_stk_base,stk_size);
p_tcb->StkPtr = p_sp;
p_tcb->StkSize = stk_size;
--------------------------------------分割线------------------------------------------------------
1.走在这一步,我又去查阅资料,参考这篇博客ARM Cortex -M 体系结构————————ARM微控制器与嵌入式系统
发现这来自于Joseph Yiu ,宋岩 译著《Cortex M3权威指南》,哈哈,差点忘了这本经典的工具书!如本书所述:
(1)R0-R7 也被称为低组寄存器。所有指令都能访问它们。它们的字长全是 32 位,复位后的初始值是不可预料的
(2)R8-R12 也被称为高组寄存器。这是因为只有很少的 16 位 Thumb 指令能访问它们,32 位的thumb-2 指令则不受限制。它们也是 32 位字长,且复位后的初始值是不可预料的。
对xPSR寄存器的解答:
这个xPSR中的x代表“任意不定状态”,可分为三个子状态寄存器APSR,IPSR,EPSR。
xPSR寄存器的位24又为什么必须为1?
—>Thumb状态,总是1,清楚此位会引起错误异常。
下面一张图我没有在《Cortex M3权威指南》,为什么会没有呢?而是在一篇博客找到答案:《嵌入式 - 嵌入式大杂烩》深入理解ARM寄存器
--------------------------------------分割线------------------------------------------------------
uC/OS-III 内核实现与应用开发实战学习(一)相关推荐
- “物联网开发实战”学习笔记-(四)智能音箱制作和语音控制
"物联网开发实战"学习笔记-(四)智能音箱制作和语音控制 这次来造一个属于自己的智能音箱,这里详细介绍了智能音箱的语音控制的实现. 智能音箱的技术架构 智能音箱主要涉及拾音.前端信 ...
- “物联网开发实战”学习笔记-(二)手机控制智能电灯
"物联网开发实战"学习笔记-(二)手机控制智能电灯 如果搭建好硬件平台后,这一次我们的任务主要是调试好智能电灯,并且连接到腾讯云的物联网平台. 腾讯云物联网平台 腾讯物联网平台的优 ...
- Oracle开发实战学习
Oracle开发实战学习 文章目录 Oracle开发实战学习 第1章 Oracle关系数据库 作业: 第2章Oracle数据库的体系结构 2.1 Oracle的物理存储结构 2.2 Oracle的逻辑 ...
- “物联网开发实战”学习笔记-(五)根据土壤湿度、环境温湿度和光照强度自动浇花
"物联网开发实战"学习笔记-(五)根据土壤湿度.环境温湿度和光照强度自动浇花 研究场景需求 自动浇花这个场景,很明显是为了自动控制水泵,及时给植物补充水分.同时用户的目的并不只是浇 ...
- 唐金州的Vue开发实战学习笔记(基础篇)
Vue开发实战学习笔记 简易的Vue程序 组件 事件 插槽 单文件组件 双向绑定 虚拟DOM与key属性 组件更新 状态data与属性props vue的响应式更新 计算属性和侦听器 计算属性 com ...
- 唐金州的Vue开发实战学习笔记(生态篇)
Vue开发实战学习笔记 Vuex Why Vuex How Vuex Vuex的核心概念和底层原理 Vuex的最佳实践 Vue Router Why Vue Router 路由类型及底层原理 Nuxt ...
- UC/OS III操作系统学习笔记
UCOS操作系统学习笔记 1.UCOSIII任务 1.1任务管理 1.2 任务创建和删除.挂起和恢复 1.3 系统内部任务 2.UCOSIII中断和时间管理 2.1 中断管理 2.2 时间管理 3.U ...
- linux底层驱动内核,Linux底层驱动开发需要学习哪些内容
Linux底层驱动开发需要学习哪些内容想必这是很多学习Linux的朋友十分头疼的问题,今天就让我来告诉大家我们到底该学习哪些内容呢? 1. 要会一些硬件知识,比如Arm接口编程 2. 学会写简单的ma ...
- python3网络爬虫开发实战学习笔记(二)------python3 XPATH爬 猫眼电影排名
我最近在看崔庆才老师的<python3 网络爬虫开发实战>觉得挺不错的,上面还有一个 用正则表达式爬取猫眼电影的电影排行榜 我练了一下,感觉不会很难,后来学到了xpath,就想用xpath ...
最新文章
- 10个linux awk文本处理经典案例,关于AWK的10个经典案例
- jenkins运行日志时间与linux,持续集成之Jenkins结合脚本实现代码自动化部署及一键回滚至上一版本...
- WCF学习笔记之可靠会话
- dbcp连接mysql,8小时会自动断开连接
- 反序列化 还是记一下吧
- 软件工程测试旅游管理系统,旅游管理系统的设计与实现
- python flask和django_真正搞明白Python中Django和Flask框架的区别
- 【转】用C#获取浏览文件夹对话框
- SQL注入 时间延时注入语句
- android英文菜单,安卓recovery菜单中英文对照
- Centos Piranha安装过程
- c语言cout函数,c++中cin与cout 详解
- Proxifier代理指定程序到fiddler
- WordPress批量修改数据库内文章内容文字关键字标题
- linux重启关机命令
- android smallestWidth 限定符屏幕适配方案dimens.xml
- hue数据导出到hdfs_如何将智能灯泡迁移到新的Philips Hue Bridge
- text-decoration
- 云仓系统或者一件代发系统
- 学术交流站点–小木虫论坛
热门文章
- 第一,永远不要跟银行借钱;第二,永远不要向民间借贷;第三,量力而行(转)...
- 【速度最快的浏览器】Chrome for Mac V75.0
- EV代码签名证书对可执行文件进行签名
- java软件开发面试常见问题,java面试技巧和注意事项
- 变焦和对焦_变焦疲劳和远程工作
- matlab函数randi,Matlab之rand(), randn(), randi()函数的使用方法
- 挂载虚拟机映像文件img
- HDU-1728---逃离迷宫(BFS)
- 基于Qt开发的网络诊断工具
- android开发方向知乎,Android开发者必看:知乎开源的图片选择库