感谢官方 免费+包邮 的 Ai8051U 实验箱。
借此实验箱学习一下 Ai8051U 的进阶操作,
在本贴记录一下学习过程。 第八集 定时器周期性任务调度 本集前面的小任务很简单,跟着做就能完成,也很好理解 重头戏是对结构体的学习,这是我之前没接触过的,这里做个笔记记录以下。 什么是结构体 结构体(Struct)是编程语言中一种复合数据类型,用于将多个不同类型的数据组合成一个逻辑 整体。它允许你将相关的变量聚合在一起,形成一个自定义的数据结构,方便统一管理和操作。 结构体的核心价值 数据聚合:将分散的变量整合成一个逻辑实体。 代码可读性:用 stu.age 比用 age_array 更直观。 函数参数传递:可以一次性传递整个结构体,而非多个分散的参数。 结构体的定义方式: 使用关键字(如 struct)定义结构体类型。 包含多个成员变量(字段),每个成员可以是不同的数据类型(如 int, float, 字符串等)。 示例(本集的程序): typedef struct { u8 Run; //任务状态:Run/Stop u16 TIMCount; //定时计数器 u16 TRITime; //重载计数器 void (*TaskHook) (void); //任务函数 } TASK_COMPONENTS; struct 的作用 定义自定义数据类型:struct 可以将多个不同类型的变量组合成一个新的复合数据类型。 数据封装:将逻辑相关的数据组织在一起。 内存管理:结构体成员在内存中连续存储。
typedef 的作用 是将TASK_COMPONENTS定义成结构体类型别名,它的核心作用是简化变量声明。 定义类型后,可以直接用TASK_COMPONENTS声明变量,无需重复写struct 关键字。 因此TASK_COMPONENTS 是类型名(类似 int、float)。而且这是一个自定义的类型别名,可以改成任何合法的,方便记忆的标识符。
static TASK_COMPONENTS Task_Comps[]= { //状态 计数 周期 函数 {0, 300, 300, LED0_Blink}, {0, 600, 600, LED1_Blink}, {0, 900, 900, LED2_Blink}, }; 这里是用类型别名TASK_COMPONENTS声明了一个数组变量Task_Comps[], 这里对3个数组元素初始化用大括号分开,每个数组元素内有四个结构体成员分别与开始定义的结构体对应。 比如{0, 300, 300, LED0_Blink}, 第一个“0” 对应结构体成员“u8 Run” 第二个“300” 对应结构体成员“u16 TIMCount” 第三个“300” 对应结构体成员“u16 TRITime” 第四个“LED0_Blink” 对应结构体成员void (*TaskHook) (void); “LED0_Blink” 是一个在其他地方声明过的执行函数。 它在其他地方是这样的 //u8 State1=0; void LED0_Blink(void) { // State1=!State1; // P00=State1; P00=!P00; } static 的作用: 作用域限制: Task_Comps 数组被声明为 static,表示它的作用域仅限于当前源文件(其他文件无法通过 extern 访问它)。 这是模块化编程中常见的封装手段,防止全局变量被意外修改。(这些内容是我用deepseek搜索得到的,我现在还没理解这里的意思,以后有机会再搞明白)
u8 Tasks_Max = sizeof(Task_Comps)/sizeof(Task_Comps[0]); 这段代码的作用是 动态计算数组长度 作用:计算整个数组 Task_Comps 占用的内存字节数。
如果 Task_Comps 是包含 3 个 TASK_COMPONENTS 结构体的数组,且单个结构体占 10 字节, 则: sizeof(Task_Comps) = 3 * 10 = 30 字节 sizeof(Task_Comps[0]) 作用:计算数组第一个元素占用的内存字节数(即单个元素的字节大小)。 单个 TASK_COMPONENTS 结构体占 10 字节: sizeof(Task_Comps[0]) = 10 字节 除法运算 sizeof(...) / sizeof(...) 作用:用数组总字节数除以单个元素字节数,得到数组的实际元素个数。 Tasks_Max = 30 / 10 = 3, 所以最终变量Tasks_Max被赋值为3。
void Task_Marks_Handler_Callback(void) { u8 i; for(i=0; i<Tasks_Max; i++) { if(Task_Comps.TIMCount) /* 如果时间不为0 */ { Task_Comps.TIMCount--; /* 时间计数器递减 */ if(Task_Comps.TIMCount == 0) /* 如果时间到了 */ { /*重载计数器值 */ Task_Comps.TIMCount = Task_Comps.TRITime; Task_Comps.Run = 1; /* 任务运行状态置1 */ } } } } 这段代码是用来操作数组Task_Comps[0]用的,每当该程序被执行时通过for循环遍历数组每个元素,通过if判断结构体中TIMCount是否为0,如果不为0,则将TIMCount 减1, 如果已经为0则将Run 置1,并且将TRITime赋值给TIMCount ,重装载倒数值。 所以函数声明后放在1ms定时器中断函数中执行,每当定时器中断时执行。 void Task_Pro_Handler_Callback(void) { u8 i; for(i=0; i<Tasks_Max; i++) { if(Task_Comps.Run) /* 检测任务状态,如果为1 则运行IF内语句 */ { Task_Comps.Run = 0; /* 将任务状态位置零 0 */ Task_Comps.TaskHook(); /* 运行程序 */ } } } 这段代码是用来执行任务的,通过for循环遍历数组每个元素,通过if判断结构体中Run 是否为1,如果为1则执行if中的语句,将任务状态位Run 置0,并运行程序。如果Run不为0 则不执行if语句。
|