前言:本篇笔记参考韦东山老师,视屏教程,连接放在最后。
同步与互斥的基本概念
同步:Task_a执行完成之后Task_b才能执行,让任务按照特定的顺序去执行。
互斥:当Task_a访问临界资源进行执行,Task_b就不能执行,反之也成立。
这两个概念的主要区别就是,同步是有特定顺序的,互斥不是,互斥是谁访问到临界资源谁就执行,没有访问到的就等待,执行结束释放资源,在此访问执行。
Task同步代码体现
static int g_LCDCanUse = 1;
static voltile int g_calc_end = 0; //实现同步的标志位
static uint64_t g_time = 0;
static uint32_t g_sum = 0;
void CalcTask(void *params)
{
uint32_t i = 0;
LCD_PrintString(0, 0, "Waiting");
g_time = system_get_ns();
for (i = 0; i < 10000000; i++)
{
g_sum += i;
}
g_calc_end = 1;//更新标志位 允许task2跑
g_time = system_get_ns() - g_time;
vTaskDelete(NULL);
}
void LcdPrintTask(void *params)
{
int len;
while (1)
{
LCD_PrintString(0, 0, "Waiting");
vTaskDelay(3000);
while (g_calc_end == 0);//判断task1,完成没
/* 打印信息 */
if (g_LCDCanUse)
{
g_LCDCanUse = 0;
LCD_ClearLine(0, 0);
len = LCD_PrintString(0, 0, "Sum: ");
LCD_PrintHex(len, 0, g_sum, 1);
LCD_ClearLine(0, 2);
len = LCD_PrintString(0, 2, "Time(ms): ");
LCD_PrintSignedVal(len, 2, g_time/1000000);
g_LCDCanUse = 1;
}
vTaskDelete(NULL);
}
}
xTaskCreate(CalcTask, "task1", 128, NULL, osPriorityNormal, NULL);
xTaskCreate(LcdPrintTask, "task2", 128, &g_Task2Info, osPriorityNormal, NULL);
这里task1和task2两个任务实现了同步,task2的执行必须在task1后面,但是这里其实还是有一点问题的,task2在等待static int g_calc_end 标志位的时候,是有cpu分配时间的,如果想让任务执行更快,可以估算Task1的执行时间,将Task2使用vTaskDelay()进行延时。
Task互斥代码体现
01 int LCD_PrintString(int x, int y, char *str)
02 {
03 static int bCanUse = 1;
04 if (bCanUse)
05 {
06 bCanUse = 0;
07 /* 使用LCD */
08 bCanUse = 1;
09 return 0;
10 }
11 return -1;
12 }
xTaskCreate(LcdPrintTask, "task1", 128, &g_Task1Info, osPriorityNormal, NULL);
xTaskCreate(LcdPrintTask, "task2", 128, &g_Task2Info, osPriorityNormal, NULL);
xTaskCreate(LcdPrintTask, "task3", 128, &g_Task3Info, osPriorityNormal, NULL);
在文件中,三个task任务使用 LcdPrintTask函数作为执行函数,但是OLED屏幕是一种互斥资源,在这里代码里面使用了标志位 g_LCDCanUse ,来防止使用Oled的过程被其他Task打断,这里通过堆标志位,进行赋值可以防止大部分情况下的Task进行切换,但是当Task1,执行到if语句,也就是第10行,这个时候切换为Task2,发现Task2也是能够正常执行的,这就会造成IIC通信时序混乱,但是这种概率很小。
01 int LCD_PrintString(int x, int y, char *str)
02 {
03 static int bCanUse = 1;
04 bCanUse--;
05 if (bCanUse == 0)
06 {
07 /* 使用LCD */
08 bCanUse++;
09 return 0;
10 }
11 else
12 {
13 bCanUse++;
14 return -1;
15 }
16 }
第一段代码中实现资源互斥,有小概率出现没有效果,是因为标志位,判断还有赋值占用时间太长了,第二段代码中,将标志位的赋值放在进入判断之前,相对于第一段代码的效果好很多,但是仍然会导致,访问Oled资源的时候无法实现互斥访问。
01 int LCD_PrintString(int x, int y, char *str)
02 {
03 static int bCanUse = 1;
04 disable_irq();
05 if (bCanUse)
06 {
07 bCanUse = 0;
08 enable_irq();
09 /* 使用LCD */
10 bCanUse = 1;
11 return 0;
12 }
13 enable_irq();
14 return -1;
15 }
上面两段代码,无法完全实现互斥访问oled资源的很大一部分原因,是因为Task之间的切换运行,也就是多Task运行,在进项判断的时候如果可以将中断关闭,不在进行Task切换,就能够完全实现,对Oled资源的互斥访问。
最后这一段代码,可以实现对Oled资源互斥,没有任何漏洞,但是在Task_b使用cpu资源进行判断是否能够执行OLed任务的时候,是一种对cpu资源的浪费,和上面同步的缺陷一样。
有没有一种办法,在task-a执行的时候,让Task_b进入阻塞状态放弃cpu资源,加快Task_a的执行,同时也可以完成Oled资源访问的互斥。
开发快捷键
这里记录一个开发快捷键技巧,当在开发的时候,需要看函数原型定义,理解这个函数干什么的时候,可以使用 ctrl+f 快捷键查找函数原型,但是这种方法会找所有相同函数名出现的地方,知道找到函数原型定义在停止,比较慢。
首先
然后直接鼠标右键,看里面的选项注释,里Go To Definition of "ADC_NTC_Temperature",跳转到函数定义,然后后面有快捷键F12,或者直接点击这个选型就可以了,这里点击这个选型进行跳转。
这个时候发现就已经跳转过来了。很实用的一个小技巧。
感谢阅读,如有错误,欢迎指正!!!
[6-1]_同步互斥与通信_有缺陷的同步示例_哔哩哔哩_bilibili