之前的章节都是针对某个或某些知识点进行的专项讲解,重点在功能和代码解释。
回到最初开始学μC/OS-III系统时,当时就定下了一个目标,不仅要读懂,还要读透,改造成更适合中国宝宝体质的使用方式。在学完野火的教程后,经过几经思考,最后决定自己锦上添花,再续上几章。
这几章想达成目的如下:
- 能够快速的上手
- 能够控制系统的功能
- 明白移植的过程
- 能够根据需要的功能来裁剪源码
从第六十一章开始的章节都是熟读源码后,根据笔者的整理方法,按照某种逻辑从系统源码中抽出来的专项解释。
笔者整理方法如下
- 各文件夹功能介绍(每个文件夹放什么文件,哪些是移植的,哪些不需要改,哪些需要修改)
- 各文件功能概览(每个文件都明白有哪些东西,是系统的哪一部分)
- 各文件函数概览(每个文件的有什么函数,函数的作用是什么,形参是什么)
- 移植的本质与移植的顺序(哪些文件需要了解,哪些文件是移植的时候需要更换的)
- 添加与裁剪源码(添功能与删功能怎么上手)
- 常用的结构体列表
- 常用宏介绍(如何用宏来控制整个系统,启用或关闭某个功能)
- main函数常用的结构顺序
- 创建任务的流程
- 任务在几种队列的变化
每个整理方法会用一章或多章的篇幅来解释。
点击此处进入μC/OS-iii章节总目录
2024.05.11:UCOSIII第六十五节:常用的控制宏介绍 第一部分
- 六十九、UCOSIII:常用的控制宏介绍 第一部分
- app_cfg.h文件
- 任务堆栈大小
- 任务优先级大小
- 最大信号量数目
- 消息队列的最大消息数量
- 每个消息的最大大小(字节)
- 代码实例
- 应用实例
- cpu_cfg.h文件
- CPU 名称配置
- CPU 时间戳配置
- CPU 中断禁用时间测量配置
- CPU计数前导零配置
- 代码实例
- 应用实例
六十九、UCOSIII:常用的控制宏介绍 第一部分
app_cfg.h文件
在本文件中定义的宏如下:
任务堆栈大小
任务优先级大小
最大信号量数目
消息队列的最大消息数量
每个消息的最大大小(字节)
代码实例
本文件代码实例如下:
#ifndef APP_CFG_H
#define APP_CFG_H
/****************************** 任务配置 *************************************/
#define APP_TASK_START_PRIO 3 // 应用程序启动任务的优先级
#define APP_TASK_START_STK_SIZE 256 // 应用程序启动任务的堆栈大小
#define TASK1_PRIO 6 // 任务1的优先级
#define TASK1_STK_SIZE 128 // 任务1的堆栈大小
#define TASK2_PRIO 7 // 任务2的优先级
#define TASK2_STK_SIZE 128 // 任务2的堆栈大小
/****************************** 信号量配置 ***********************************/
#define MAX_SEM 5 // 最大信号量数目
/****************************** 消息队列配置 **********************************/
#define MAX_QUEUE_ENTRIES 10 // 消息队列的最大消息数量
#define MAX_MSG_SIZE 32 // 每个消息的最大大小(字节)
#endif /* APP_CFG_H */
应用实例
使用app_cfg.h中定义的配置参数在应用程序中创建任务、初始化信号量和消息队列:
#include "os.h"
#include "app_cfg.h"
// 任务函数原型
void Task1(void *p_arg);
void Task2(void *p_arg);
// 信号量
OS_SEM sem1;
// 消息队列
OS_Q queue1;
int main(void) {
OS_ERR err;
// 初始化操作系统
OSInit(&err);
// 创建任务
OSTaskCreate((OS_TCB *)&Task1TCB,
(CPU_CHAR *)"Task 1",
Task1,
0,
TASK1_PRIO,
&Task1Stk[0],
TASK1_STK_SIZE / 10,
TASK1_STK_SIZE,
0,
0,
0,
(OS_OPT_TASK_STK_CHK | OS_OPT_TASK_STK_CLR),
&err);
OSTaskCreate((OS_TCB *)&Task2TCB,
(CPU_CHAR *)"Task 2",
Task2,
0,
TASK2_PRIO,
&Task2Stk[0],
TASK2_STK_SIZE / 10,
TASK2_STK_SIZE,
0,
0,
0,
(OS_OPT_TASK_STK_CHK | OS_OPT_TASK_STK_CLR),
&err);
// 创建信号量
OSSemCreate(&sem1, (CPU_CHAR *)"Semaphore 1", 0, &err);
// 创建消息队列
OSQCreate(&queue1, (CPU_CHAR *)"Queue 1", MAX_QUEUE_ENTRIES, &err);
// 启动操作系统
OSStart(&err);
return 0;
}
// 任务函数
void Task1(void *p_arg) {
OS_ERR err;
while (1) {
// 任务1的代码
// 例如:等待信号量
OSSemPend(&sem1, 0, OS_OPT_PEND_BLOCKING, NULL, &err);
// 执行其他操作
}
}
void Task2(void *p_arg) {
OS_ERR err;
while (1) {
// 任务2的代码
// 例如:向消息队列发送消息
CPU_INT08U msg = 10;
OSQPost(&queue1, &msg, sizeof(msg), OS_OPT_POST_FIFO, &err);
// 执行其他操作
}
}
cpu_cfg.h文件
cpu_cfg.h文件通常包含一些针对特定处理器和编译器的配置选项和宏定义。
这些配置选项可以根据你的硬件平台和应用程序需求进行自定义。一般情况下,cpu_cfg.h 文件可能包含以下内容:
CPU 名称配置
CPU 时间戳配置
CPU 中断禁用时间测量配置
CPU计数前导零配置
代码实例
/*
*********************************************************************************************************
* CPU 名称配置
*
* 注意事项:(1) 配置 CPU_CFG_NAME_EN 以启用/禁用 CPU 主机名特性:
*
* (a) CPU 主机名存储
* (b) CPU 主机名 API 函数
*
* (2) 使用所需的 ASCII 字符串大小配置 CPU_CFG_NAME_SIZE,包括终止的 NULL 字符。
*
* 参见 'cpu_core.h 全局变量 注意事项 #1'。
*********************************************************************************************************
*/
//CPU 名称配置:
//CPU_CFG_NAME_EN: 用于启用或禁用 CPU 主机名特性。
//CPU_CFG_NAME_SIZE: 配置 CPU 主机名的 ASCII 字符串大小,包括终止的 NULL 字符。
/* 配置 CPU 主机名特性(参见注意事项 #1): */
#define CPU_CFG_NAME_EN DEF_ENABLED
/* DEF_DISABLED CPU 主机名已禁用 */
/* DEF_ENABLED CPU 主机名已启用 */
/* 配置 CPU 主机名 ASCII 字符串大小 ... */
#define CPU_CFG_NAME_SIZE 16u /* ...(参见注意事项 #2)。 */
/*$PAGE*/
/*
*********************************************************************************************************
* CPU 时间戳配置
*
* 注意事项:(1) 配置 CPU_CFG_TS_xx_EN 以启用/禁用 CPU 时间戳功能:
*
* (a) CPU_CFG_TS_32_EN 启用/禁用 32 位 CPU 时间戳功能
* (b) CPU_CFG_TS_64_EN 启用/禁用 64 位 CPU 时间戳功能
*
* (2) (a) 使用 CPU_CFG_TS_TMR_SIZE 配置 CPU 时间戳定时器的字大小:
*
* CPU_WORD_SIZE_08 8 位字大小
* CPU_WORD_SIZE_16 16 位字大小
* CPU_WORD_SIZE_32 32 位字大小
* CPU_WORD_SIZE_64 64 位字大小
*
* (b) 如果 CPU 时间戳定时器的大小不是 8 位八位组的二进制倍数(例如 20 位或偶数 24 位),
* 则应配置为下一个较低的二进制倍数的八位组大小(例如 16 位)。
* 然而,CPU 时间戳定时器的最小支持字大小为 8 位。
*
* 参见 'cpu_core.h 函数原型 CPU_TS_TmrRd() 注意事项 #2a'。
*********************************************************************************************************
*/
//CPU 时间戳配置:
//CPU_CFG_TS_32_EN 和 CPU_CFG_TS_64_EN: 启用或禁用 32 位和 64 位 CPU 时间戳功能。
//CPU_CFG_TS_TMR_SIZE: 配置 CPU 时间戳定时器的字大小。可以是 8 位、16 位、32 位或 64 位。
/* 配置 CPU 时间戳功能(参见注意事项 #1): */
#define CPU_CFG_TS_32_EN DEF_ENABLED // Modified by fire (原是 DEF_DISABLED)
#define CPU_CFG_TS_64_EN DEF_DISABLED
/* DEF_DISABLED CPU 时间戳已禁用 */
/* DEF_ENABLED CPU 时间戳已启用 */
/* 配置 CPU 时间戳定时器字大小 ... */
/* ...(参见注意事项 #2): */
#define CPU_CFG_TS_TMR_SIZE CPU_WORD_SIZE_32
/*
*********************************************************************************************************
* CPU 中断禁用时间测量配置
*
* 注意事项:(1) (a) 配置 CPU_CFG_INT_DIS_MEAS_EN 以启用/禁用测量 CPU 中断禁用时间:
*
* (a) 已启用, 如果在 'cpu_cfg.h' 中 CPU_CFG_INT_DIS_MEAS_EN #define'd
*
* (b) 已禁用, 如果在 'cpu_cfg.h' 中 CPU_CFG_INT_DIS_MEAS_EN 未 #define'd
*
* 参见 'cpu_core.h 函数原型 注意事项 #1'
* & 'cpu_core.h CPU 包含文件 注意事项 #3'。
*
* (b) 使用 CPU_CFG_INT_DIS_MEAS_OVRHD_NBR 配置要测量和平均中断禁用时间测量开销的次数。
*
* 建议仅进行一次测量,即使对于启用指令高速缓存的 CPU,临界区也不会在指令缓存循环中调用。
* 因此,对于大多数非缓存中断禁用时间测量,单次非缓存/非平均时间测量是更现实的开销。
*
* 参见 'cpu_core.c CPU_IntDisMeasInit() 注意事项 #3a'。
*********************************************************************************************************
*/
//CPU 中断禁用时间测量配置:
//CPU_CFG_INT_DIS_MEAS_EN: 启用或禁用测量 CPU 中断禁用时间的功能。
//CPU_CFG_INT_DIS_MEAS_OVRHD_NBR: 配置测量中断禁用时间开销的次数。建议仅测量一次开销。
#if 1 // Modified by fire (原是 0) /* 配置 CPU 中断禁用时间 ... */
#define CPU_CFG_INT_DIS_MEAS_EN /* ... 测量功能(参见注意事项 #1a)。 */
#endif
/* 配置中断禁用开销测量的次数 ... */
#define CPU_CFG_INT_DIS_MEAS_OVRHD_NBR 1u /* ... 时间测量(参见注意事项 #1b)。 */
/*$PAGE*/
/*
*********************************************************************************************************
* CPU计数前导零配置
*
* 注意事项:(1) 配置 CPU_CFG_LEAD_ZEROS_ASM_PRESENT 以在以下位置原型/定义计数前导零位数功能:
*
* (a) 'cpu.h'/'cpu_a.asm', 如果在 'cpu.h'/'cpu_cfg.h' 中 CPU_CFG_LEAD_ZEROS_ASM_PRESENT #define'd,则启用汇编版本的功能
*
* (b) 'cpu_core.h'/'cpu_core.c',如果在 'cpu.h'/'cpu_cfg.h' 中 CPU_CFG_LEAD_ZEROS_ASM_PRESENT 未 #define'd,则启用 C 源码版本的功能
*********************************************************************************************************
*/
//CPU 前导零计数配置:
//CPU_CFG_LEAD_ZEROS_ASM_PRESENT: 启用汇编版本或 C 源码版本的计数前导零位数函数。
#if 1 /* 配置 CPU 计数前导零位数 ... */
#define CPU_CFG_LEAD_ZEROS_ASM_PRESENT /* ... 汇编版本(参见注意事项 #1)。 */
#endif
应用实例
好的,下面是一个简单的示例,展示了如何在代码中使用上述 cpu_cfg.h
中定义的一些宏:
#include <cpu_cfg.h>
#include <os.h>
#define TASK_PRIO_HIGH 10
#define TASK_PRIO_MEDIUM 15
#define TASK_PRIO_LOW 20
#define TASK_STACK_SIZE 256
// 定义任务堆栈
static CPU_STK task_stk_high[TASK_STACK_SIZE];
static CPU_STK task_stk_medium[TASK_STACK_SIZE];
static CPU_STK task_stk_low[TASK_STACK_SIZE];
// 任务函数
void TaskHighPrio(void *p_arg);
void TaskMediumPrio(void *p_arg);
void TaskLowPrio(void *p_arg);
int main(void) {
OS_ERR os_err;
// 初始化uC/OS-III内核
OSInit(&os_err);
// 创建任务
OSTaskCreate(&TaskHighPrio,
(void *)0,
&task_stk_high[TASK_STACK_SIZE - 1],
TASK_PRIO_HIGH);
OSTaskCreate(&TaskMediumPrio,
(void *)0,
&task_stk_medium[TASK_STACK_SIZE - 1],
TASK_PRIO_MEDIUM);
OSTaskCreate(&TaskLowPrio,
(void *)0,
&task_stk_low[TASK_STACK_SIZE - 1],
TASK_PRIO_LOW);
// 启动uC/OS-III内核
OSStart(&os_err);
return 0;
}
// 高优先级任务函数
void TaskHighPrio(void *p_arg) {
while (1) {
// 高优先级任务逻辑
OSTimeDlyHMSM(0, 0, 0, 500, OS_OPT_TIME_HMSM_STRICT, NULL);
}
}
// 中优先级任务函数
void TaskMediumPrio(void *p_arg) {
while (1) {
// 中优先级任务逻辑
OSTimeDlyHMSM(0, 0, 1, 0, OS_OPT_TIME_HMSM_STRICT, NULL);
}
}
// 低优先级任务函数
void TaskLowPrio(void *p_arg) {
while (1) {
// 低优先级任务逻辑
OSTimeDlyHMSM(0, 0, 2, 0, OS_OPT_TIME_HMSM_STRICT, NULL);
}
}
在这个示例中,我们使用了 cpu_cfg.h
中定义的宏来设置任务的优先级和堆栈大小。同时,任务函数中的 OSTimeDlyHMSM
函数用于模拟任务执行的延迟。这里只是一个简单的示例,实际的应用中,你需要根据具体的需求来编写任务函数和任务创建的逻辑。