上节课问题
一、嵌入式开发
| 裸机开发 |
调度器开发 |
| 执行模式是阻塞性的 |
调度器开发就是非阻塞性的 是并行的,也就是freeRots Rtthead |
单车道公路 一辆车一辆车进行 |
繁忙的餐厅 有服务员进行调度,选择服务哪个顾客 |
| 效率高一些 |
必须规避全局变量的满天飞 |
| 实时性要求高的场景 |
|
| 功耗低的场景 |
|
| 极简设备的场景 |
|
二、工程模版的建立
1. 建立步骤
- 结构体定义
- 结构体内存布局
- 变量声明与初始化(我定义之后肯定要求声明,这个定义结构体和普通的定义变量还有区别,就是说它定义结构体定义的是一种数据类型,还忒再按照这个类型去声明)
1 2 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访问的是结构体指针的成员
1 2 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语言进行补充:
1 2 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);
1 2 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; }
|
1 2 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; }
|
所以指针传递可以改变原始数据,也就是传进来的数据
指针传递的另一个优点:
效率高
1 2 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.调度器结构体详解
1 2 3 4 5 6
| typedef struct { void (*task_func)(void); uint32_t rate_ms; uint32_t last_run; } scheduler_task_t;
|
1 2 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);
|
- 问题:为什么普通指针(包括结构体指针)需要解码后引用,函数指针不需要解码后引用,函数指针不需要加*
1 2 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. 实际上两种写法完全等价
1 2
| my_function_ptr(1000); (*my_function_ptr)(1000);
|
本质上,数据指针指向的是内存,而函数指针就是一个入口点,直接就可以使用,使用时不会产生歧义
具体分析:
- task_func: void (*task_func)(void)
函数指针,指向任务的执行函数,在调度时被调用
- rate_ms任务执行周期
- last_run记录上一次执行的时间戳
5.具体代码
1 2 3 4 5 6 7
| typedef struct { void (*task_func)(void); uint32_t rate_ms; uint32_t last_run; } scheduler_task_t;
|
1 2 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} };
|
1 2 3 4 5 6 7 8 9 10
|
void scheduler_init(void) { task_num = sizeof(scheduler_task) / sizeof(scheduler_task_t); }
|
1 2 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()函数的调用使用
- 后面的代码为什么就不解释了,因为我感觉实战中会用就行了
三、实例代码:
1 2 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. 优先级调度
1 2 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. 低功耗管理
1 2 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. 动态任务管理
1 2 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模版修改为调度器模版