上节课问题
用谷歌的最新模型问:
	提问方式:背景+讲解+问题 问题越详细越好
举例:
    我是一个刚学习嵌入式开发的小白 请你帮我讲解一下什么是硬件抽象层 什么是HAL库 同样都是调用API 为什么HAL库比标准库更好一致
答案:
	HAL就是一个逻辑层 1. 它像一个翻译官或者适配器,提供接口 2.提高了可移植性 
举例:1.点灯 不需要再管GPIO的模型了
- F0系列的代码很容易移植到F4系列
一、任务调度器概念
- 为什么要减速:因为while循环扫描太快了,我们不需要那么频繁的执行外设程序
准确的告诉你什么时候干什么,官家
我们嵌入式资源有限,肯定要间断执行
- 定时执行任务(按键10ms 数码管50ms 超声波100ms ADDA 80ms ds1302160ms ds18b20 300ms)
- 循环执行(都在while里面都是循环的)
- 非阻塞设计(没有while)
- 模块化编程
二、如何撰写任务调度器
1. C语言结构体基础
- 结构体定义
- 结构体内存布局
- 变量声明与初始化(我定义之后肯定要求声明,这个定义结构体和普通的定义变量还有区别,就是说它定义结构体定义的是一种数据类型,还忒再按照这个类型去声明)
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 
 | 
 struct Task{
 
 
 };
 struct Task task1={"",""}
 
 
 typedef struct{
 
 }Task_t
 Task_t task2={"",""}
 
 Task_t task3 ={
 "name" = "压力检测",
 "priority" = 3;
 }
 
 | 
2.访问结构体成员
!!!点task1.name访问的就是结构体成员,而箭头运算符task->name访问的是结构体指针的成员
| 12
 3
 4
 5
 6
 7
 8
 
 | Task_t task={“显示刷新",5};
 printf(“任务名称:%s\n",task.name);
 task.priority=4;
 
 Task_t *p_task = &task;
 printf(“任务优先级:%d\n",p_task->priority);
 p_task->priority++;//增加优先级
 
 | 
现在对我的C语言进行补充:
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 
 | 
 
 
 
 int num  = 10;
 int *p_num = #
 *p_num = 20;
 printf("%d",*p_num);
 
 
 
 Task_t task = {"显示刷新", 5};
 Task_t *p_task = &task;
 p_task->priority = 4;
 
 (*p_task).priority = 4;
 
 
 | 
函数有时需要传入参数进行处理,有的函数只需要传入数据(也就是变量)进行运算然后返回一个特定的值,在这种情况下,我不需要修改传入参数的值,所以我直接传入变量
蓝桥杯里:float rd_temperature   unsigned int Ut_Wave_Data
而有些函数需要修改我传入数据的值,这时候就需要传入指针,最典型的例子:
Read_Rtc(*Rtc);
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 
 | #include <stdio.h>
 
 
 int square(int num) {
 int result = num * num;
 num = 0;
 return result;
 }
 
 
 typedef struct {
 char name[50];
 int priority;
 } Task_t;
 
 
 void displayTask(Task_t task) {
 printf("任务名称: %s, 优先级: %d\n", task.name, task.priority);
 task.priority = 99;
 }
 
 int main() {
 int x = 5;
 printf("x的平方是: %d\n", square(x));
 printf("函数调用后x的值: %d\n", x);
 
 Task_t myTask = {"数据备份", 3};
 displayTask(myTask);
 printf("函数调用后优先级: %d\n", myTask.priority);
 
 return 0;
 }
 
 
 | 
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 
 | #include <stdio.h>
 
 
 void swap(int *a, int *b) {
 int temp = *a;
 *a = *b;
 *b = temp;
 }
 
 
 typedef struct {
 char name[50];
 int priority;
 } Task_t;
 
 
 void upgradePriority(Task_t *task) {
 
 task->priority += 1;
 printf("任务 \"%s\" 优先级已提升到 %d\n", task->name, task->priority);
 }
 
 int main() {
 int m = 10, n = 20;
 printf("交换前: m = %d, n = %d\n", m, n);
 
 swap(&m, &n);
 printf("交换后: m = %d, n = %d\n", m, n);
 
 Task_t myTask = {"系统更新", 2};
 printf("原始优先级: %d\n", myTask.priority);
 
 upgradePriority(&myTask);
 printf("函数调用后优先级: %d\n", myTask.priority);
 
 return 0;
 }
 
 
 | 
所以指针传递可以改变原始数据,也就是传进来的数据
指针传递的另一个优点:
效率高
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 
 | >#include <stdio.h>>#include <time.h>
 
 >typedef struct {
 int data[10000];
 char text[50000];
 >} BigStruct_t;
 
 >void processByValue(BigStruct_t data) {
 
 data.data[0] += 1;
 >}
 
 >void processByPointer(BigStruct_t *data) {
 
 data->data[0] += 1;
 >}
 
 >int main() {
 BigStruct_t bigData = {0};
 clock_t start, end;
 int i, iterations = 100000;
 
 
 start = clock();
 for(i = 0; i < iterations; i++) {
 processByValue(bigData);
 }
 end = clock();
 printf("值传递耗时: %f 秒\n", (double)(end - start) / CLOCKS_PER_SEC);
 
 
 start = clock();
 for(i = 0; i < iterations; i++) {
 processByPointer(&bigData);
 }
 end = clock();
 printf("指针传递耗时: %f 秒\n", (double)(end - start) / CLOCKS_PER_SEC);
 
 return 0;
 >}
 
 | 
实验结果:
指针传递用时:几秒钟
值传递用时:几分钟
3. 结构体拓展
结构体嵌套以及结构体数组//暂时先不重点了解,等到后面深入了再了解
4.调度器结构体详解
| 12
 3
 4
 5
 6
 
 | typedef struct {
 void (*task_func)(void);
 uint32_t rate_ms;
 uint32_t last_run;
 } scheduler_task_t;
 
 | 
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 
 | 
 int *p_num;
 
 Task *p_task;
 
 void (*task_func)(void);
 
 void task_func(void) {
 
 }
 
 void task_func(int time){}
 
 void (*task_func_ptr)(int)
 
 void (*my_function_ptr)(int) = task_func;
 
 my_function_ptr(1000);
 
 | 
- 问题:为什么普通指针(包括结构体指针)需要解码后引用,函数指针不需要解码后引用,函数指针不需要加*
| 12
 3
 4
 5
 6
 7
 8
 9
 
 | int x = 10;
 int *p_num = &x;
 *p_num = 20;
 
 
 void (*my_function_ptr)(int) = task_func;
 my_function_ptr(1000);
 
 
 | 
解答:1. 语法上便利   2. 实际上两种写法完全等价
| 12
 
 | my_function_ptr(1000);     (*my_function_ptr)(1000);
 
 | 
本质上,数据指针指向的是内存,而函数指针就是一个入口点,直接就可以使用,使用时不会产生歧义
具体分析:
- task_func:    void (*task_func)(void)
       函数指针,指向任务的执行函数,在调度时被调用
- rate_ms任务执行周期
- last_run记录上一次执行的时间戳
5.具体代码
| 12
 3
 4
 5
 6
 7
 
 | typedef struct {
 void (*task_func)(void);
 uint32_t rate_ms;
 uint32_t last_run;
 } scheduler_task_t;
 
 
 | 
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 
 | 
 
 uint8_t task_num;
 
 
 static scheduler_task_t scheduler_task[] =
 {
 {Led_Proc, 1, 0},
 {Key_Proc, 10, 0},
 {Sensor_Proc, 100, 0},
 {Comm_Proc, 50, 0}
 };
 
 | 
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 
 | 
 
 
 
 void scheduler_init(void)
 {
 
 task_num = sizeof(scheduler_task) / sizeof(scheduler_task_t);
 }
 
 | 
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 
 | 
 
 
 void scheduler_run(void)
 {
 
 for (uint8_t i = 0; i < task_num; i++)
 {
 
 uint32_t now_time = HAL_GetTick();
 
 
 if (now_time >= scheduler_task[i].rate_ms + scheduler_task[i].last_run)
 {
 
 scheduler_task[i].last_run = now_time;
 
 
 scheduler_task[i].task_func();
 }
 }
 }
 
 | 
HAL_GetTick()函数的调用使用
- 后面的代码为什么就不解释了,因为我感觉实战中会用就行了
三、实例代码:
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 
 | #include "scheduler.h"
 
 uint8_t task_num;
 
 typedef struct {
 void (*task_func)(void);
 uint32_t rate_ms;
 uint32_t last_run;
 } task_t;
 
 
 void led_proc(void);
 void key_proc(void);
 void sensor_proc(void);
 void comm_proc(void);
 
 
 static task_t scheduler_task[] =
 {
 {led_proc, 1000, 0},
 {key_proc, 10, 0},
 {sensor_proc, 100, 0},
 {comm_proc, 50, 0}
 };
 
 
 
 
 
 void scheduler_init(void)
 {
 
 task_num = sizeof(scheduler_task) / sizeof(task_t);
 }
 
 
 
 
 
 void scheduler_run(void)
 {
 
 for (uint8_t i = 0; i < task_num; i++)
 {
 
 uint32_t now_time = HAL_GetTick();
 
 
 if (now_time >= scheduler_task[i].rate_ms + scheduler_task[i].last_run)
 {
 
 scheduler_task[i].last_run = now_time;
 
 
 scheduler_task[i].task_func();
 }
 }
 }
 
 
 void led_proc(void)
 {
 
 HAL_GPIO_TogglePin(LED_GPIO_Port, LED_Pin);
 }
 
 void key_proc(void)
 {
 
 if (HAL_GPIO_ReadPin(KEY_GPIO_Port, KEY_Pin) == GPIO_PIN_RESET)
 {
 
 }
 }
 
 void sensor_proc(void)
 {
 
 uint16_t sensor_value = HAL_ADC_GetValue(&hadc1);
 
 
 }
 
 void comm_proc(void)
 {
 
 if (HAL_UART_GetState(&huart1) == HAL_UART_STATE_READY)
 {
 
 }
 }
 
 
 | 
四、进阶代码
1. 优先级调度
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 
 | 
 typedef struct {
 void (*task_func)(void);
 uint32_t rate_ms;
 uint32_t last_run;
 uint8_t priority;
 } priority_task_t;
 
 
 void sort_tasks_by_priority(void) {
 
 for (uint8_t i = 0; i < task_num - 1; i++) {
 for (uint8_t j = 0; j < task_num - i - 1; j++) {
 if (scheduler_task[j].priority > scheduler_task[j + 1].priority) {
 
 priority_task_t temp = scheduler_task[j];
 scheduler_task[j] = scheduler_task[j + 1];
 scheduler_task[j + 1] = temp;
 }
 }
 }
 }
 
 | 
2. 低功耗管理
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 
 | 
 
 void scheduler_run(void)
 {
 bool all_tasks_idle = true;
 uint32_t time_to_next_task = UINT32_MAX;
 uint32_t now_time = HAL_GetTick();
 
 
 for (uint8_t i = 0; i < task_num; i++)
 {
 uint32_t time_to_task = (scheduler_task[i].last_run +
 scheduler_task[i].rate_ms) - now_time;
 
 if (time_to_task == 0) {
 
 scheduler_task[i].last_run = now_time;
 scheduler_task[i].task_func();
 all_tasks_idle = false;
 } else if (time_to_task < time_to_next_task) {
 
 time_to_next_task = time_to_task;
 }
 }
 
 
 if (all_tasks_idle && time_to_next_task > MIN_SLEEP_TIME) {
 
 HAL_PWR_EnterSLEEPMode(PWR_MAINREGULATOR_ON, PWR_SLEEPENTRY_WFI);
 }
 }
 
 
 | 
3. 动态任务管理
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 
 | 
 #define MAX_TASKS 10
 
 typedef struct {
 void (*task_func)(void);
 uint32_t rate_ms;
 uint32_t last_run;
 bool active;
 } dynamic_task_t;
 
 dynamic_task_t scheduler_task[MAX_TASKS];
 uint8_t task_num = 0;
 
 
 uint8_t add_task(void (*task_func)(void), uint32_t rate_ms) {
 if (task_num >= MAX_TASKS) {
 return 0xFF;
 }
 
 scheduler_task[task_num].task_func = task_func;
 scheduler_task[task_num].rate_ms = rate_ms;
 scheduler_task[task_num].last_run = HAL_GetTick();
 scheduler_task[task_num].active = true;
 
 return task_num++;
 }
 
 
 bool remove_task(uint8_t task_id) {
 if (task_id >= task_num) {
 return false;
 }
 
 
 for (uint8_t i = task_id; i < task_num - 1; i++) {
 scheduler_task[i] = scheduler_task[i + 1];
 }
 
 task_num--;
 return true;
 }
 
 
 bool pause_task(uint8_t task_id) {
 if (task_id >= task_num) {
 return false;
 }
 
 scheduler_task[task_id].active = false;
 return true;
 }
 
 
 bool resume_task(uint8_t task_id) {
 if (task_id >= task_num) {
 return false;
 }
 
 scheduler_task[task_id].active = true;
 scheduler_task[task_id].last_run = HAL_GetTick();
 return true;
 }
 
 
 
 | 
课下作业
- 为什么for循环中的两句代码不能调换顺序 
- 将V4T模版修改为调度器模版