STM32CubeMX学习笔记22---FreeRTOS(任务创建和删除)

 一、FreeRTOS简介

FreeRTOS 是一个可裁剪、可剥夺型的多任务内核,而且没有任务数限制。FreeRTOS 提供了实时操作系统所需的所有功能,包括资源管理、同步、任务通信等。

FreeRTOS 是用 C 和汇编来写的,其中绝大部分都是用 C 语言编写的,只有极少数的与处理器密切相关的部分代码才是用汇编写的,FreeRTOS 结构简洁,可读性很强!最主要的是非常适合初次接触嵌入式实时操作系统学生、嵌入式系统开发人员和爱好者学习。

最新版本 V9.0.0(2016年),尽管现在 FreeRTOS 的版本已经更新到 V10.4.1 了,但是我们还是选择 V9.0.0,因为内核很稳定,并且网上资料很多,因为 V10.0.0 版本之后是亚马逊收购了FreeRTOS之后才出来的版本,主要添加了一些云端组件,一般采用 V9.0.0 版本足以。

FreeRTOS官网:http://www.freertos.org/
代码托管网站:https://sourceforge.net/projects/freertos/files/FreeRTOS/

RTOS的多个工作流如下图示:

RTOS工作原理如下图示:

RTOS通用组件如下图示:

RTOS的特点:

  • 更好的事件实时处理机制
  • 更高效利用CPU资源
  • 通用的任务管理框架

FreeRTOS介绍

FreeRTOS是RTOS的一种,尺寸非常小,可运行于微控制器上。微控制器是尺寸小,资源受限的处理器,它在单个芯片上包含了处理器本身、用于保存要执行的程序的只读存储器(ROM或Flash)、所执行程序需要的随机存取存储器(RAM),一般情况下程序直接从只读存储器执行

微控制器用于深度嵌入式应用,一般都有非常明确、专门的工作。尺寸的限制以及专用的终端应用等性质,令其很少能使用完整的RTOS实现。因此FreeRTOS仅为内核提供了实时调度功能、任务间通信、时序和同步原语。更准确地说,它是一个实时内核,或实时执行器。命令控制台界面、网络栈等额外的功能可作为附加组件

在实际使用FreeRTOS的时候我们需要根据自已的需求来配置FreeRTOS,不同架构的MCU在使用的时候配置也不同,下面介绍FreeRTOS配置文件详解

  • 内核配置一

  • 内核配置二

  • 内存管理

  • 任务运行信息获取配置

  • 软件定时器

  • 中断优先级

  • 函数Include配置

二、硬件设计

本实验通过freertos创建两个任务来分别控制LED2和LED3的亮灭,需要用到的硬件资源

  • LED2和LED3指示灯
  • 串口

三、STM32CubeMX设置

1、基础设置

  • RCC设置外接HSE,时钟设置为72MHz;TIM3的时钟挂载在APB1 Time Clocks上为72MHz。
  • 打开串口中断,波特率设置为115200. 
  • PB5/PE1设置为GPIO推挽输出模式、上拉、高速、默认输出电平为高电平。

2、FreeRtos设置

 (1)、在 System Core 中选择 SYS ,对 Timebase Source 进行设置,选择 TIM1 作为HAL库的时基(除了 SysTick 外都可以)。

在基于STM32 HAL的项目中,一般需要维护的 “时基” 主要有2个:

  1. HAL的时基,SYS Timebase Source
  2. OS的时基(仅在使用OS的情况下才考虑)

而这些 “时基” 该去如何维护,主要分为两种情况考虑:

裸机运行:
可以通过 SysTick(滴答定时器)或 (TIMx)定时器 的方式来维护 SYS Timebase Source,也就是HAL库中的 uwTick,这是HAL库中维护的一个全局变量。在裸机运行的情况下,我们一般选择默认的 SysTick(滴答定时器) 方式即可,也就是直接放在 SysTick_Handler() 中断服务函数中来维护。

带OS运行:
前面提到的 SYS Timebase Source 是STM32的HAL库中的新增部分,主要用于实现 HAL_Delay() 以及作为各种 timeout 的时钟基准。

在使用了OS(操作系统)之后,OS的运行也需要一个时钟基准(简称“时基”),来对任务和时间等进行管理。而OS的这个 时基 一般也都是通过 SysTick(滴答定时器) 来维护的,这时就需要考虑 “HAL的时基” 和 “OS的时基” 是否要共用 SysTick(滴答定时器) 了。

如果共用SysTick,当我们在CubeMX中选择启用FreeRTOS之后,在生成代码时,CubeMX一定会报如下提示:

强烈建议用户在使用FreeRTOS的时候,不要使用 SysTick(滴答定时器)作为 “HAL的时基”,因为FreeRTOS要用,最好是要换一个!!!如果共用,潜在一定风险。 

 (2)、在 Middleware 中选择 FREERTOS 设置,并选择 CMSIS_V1 接口版本

CMSIS是一种接口标准,目的是屏蔽软硬件差异以提高软件的兼容性。RTOS v1使得软件能够在不同的实时操作系统下运行(屏蔽不同RTOS提供的API的差别),而RTOS v2则是拓展了RTOS v1,兼容更多的CPU架构和实时操作系统。因此我们在使用时可以根据实际情况选择,如果学习过程中使用STM32F1、F4等单片机时没必要选择RTOS v2,更高的兼容性背后时更加冗余的代码,理解起来比较困难。

(3)、在 Config parameters 进行具体参数配置。

Kernel settings:

  • USE_PREEMPTION Enabled:RTOS使用抢占式调度器;Disabled:RTOS使用协作式调度器(时间片)。
  • TICK_RATE_HZ: 值设置为1000,即周期就是1ms。RTOS系统节拍中断的频率,单位为HZ。
  • MAX_PRIORITIES: (默认):可使用的最大优先级数量。设置好以后任务就可以使用从0到(MAX_PRIORITIES - 1)的优先级,其中0位最低优先级,(MAX_PRIORITIES - 1)为最高优先级。
  • MINIMAL_STACK_SIZE:(默认128): 设置空闲任务的最小任务堆栈大小,以字为单位,而不是字节。如该值设置为128 Words,那么真正的堆栈大小就是 128*4 = 512 Byte。
  • MAX_TASK_NAME_LEN:(默认16): 设置任务名称最大长度。
  • IDLE_SHOULD_YIELD: 默认Enabled 空闲任务放弃CPU使用权给其他同优先级的用户任务。
  • USE_MUTEXES:默认Enabled 为1时使用互斥信号量,相关的API函数会被编译。
  • USE_RECURSIVE_MUTEXES默认Enabled 为1时使用递归互斥信号量,相关的API函数会被编译。
  • USE_COUNTING_SEMAPHORES默认Enabled 为1时启用计数型信号量, 相关的API函数会被编译。
  • QUEUE_REGISTRY_SIZE(默认8) 设置可以注册的队列和信号量的最大数量,在使用内核调试器查看信号量和队列的时候需要设置此宏,而且要先将消息队列和信号量进行注册,只有注册了的队列和信号量才会在内核调试器中看到,如果不使用内核调试器的话此宏设置为0即可。
  • USE_APPLICATION_TASK_TAG(默认Disable) 为1时可以使用vTaskSetApplicationTaskTag函数。
  • ENABLE_BACKWARD_COMPATIBILITY(默认Enabled ) 为1时可以使V8.0.0之前的FreeRTOS用户代码直接升级到V8.0.0之后,而不需要做任何修改。
  • USE_PORT_OPTIMISED_TASK_SELECTION(默认Enabled ) FreeRTOS有两种方法来选择下一个要运行的任务,一个是通用的方法,另外一个是特殊的方法,也就是硬件方法,使用MCU自带的硬件指令来实现。STM32有计算前导零指令吗,所以这里强制置1。
  • USE_TICKLESS_IDLE 置1(Built in functionality enabled):使能低功耗tickless模式;置0:保持系统节拍(tick)中断一直运行。假设开启低功耗的话可能会导致下载出现问题,因为程序在睡眠中,可用ISP下载办法解决。
  • USE_TASK_NOTIFICATIONS(默认Enabled )为1时使用任务通知功能,相关的API函数会被编译。开启了此功能,每个任务会多消耗8个字节。
  • RECORD_STACK_HIGH_ADDRESS(默认Disable) 为1时栈开始地址会被保存到每个任务的TCB中(假如栈是向下生长的)。

Memory management settings:

  • Memory Allocation: Dynamic/Static 支持动态/静态内存申请
  • TOTAL_HEAP_SIZE默认,设置堆大小,如果使用了动态内存管理,FreeRTOS在创建 task, queue, mutex, software timer or semaphore的时候就会使用heap_x.c(x为1~5)中的内存申请函数来申请内存。这些内存就是从堆ucHeap[configTOTAL_HEAP_SIZE]中申请的。
  • Memory Management scheme: 内存管理策略 heap_4

Hook function related definitions:

  • USE_IDLE_HOOK: 默认, 置1:使用空闲钩子(Idle Hook类似于回调函数);置0:忽略空闲钩子。
  • USE_TICK_HOOK默认, 置1:使用时间片钩子(Tick Hook);置0:忽略时间片钩子。
  • USE_MALLOC_FAILED_HOOK默认, 使用内存申请失败钩子函数。
  • CHECK_FOR_STACK_OVERFLOW默认, 大于0时启用堆栈溢出检测功能,如果使用此功能用户必须提供一个栈溢出钩子函数,如果使用的话此值可以为1或者2,因为有两种栈溢出检测方法。

Run time and task stats gathering related definitions:

  • GENERATE_RUN_TIME_STATS: 启用运行时间统计功能。
  • USE_TRACE_FACILITY: 启用可视化跟踪调试。
  • USE_STATS_FORMATTING_FUNCTIONS: 与宏configUSE_TRACE_FACILITY同时为1时会编译下面3个函数prvWriteNameToBuffer()、vTaskList()、vTaskGetRunTimeStats()。

Co-routine related definitions:

  • USE_CO_ROUTINES: 启用协程Enabled
  • MAX_CO_ROUTINE_PRIORITIES: 协程的有效优先级数目2

Software timer definitions:

  • USE_TIMERS: 启用软件定时器Enabled

Interrupt nesting behaviour configuration:

  • LIBRARY_LOWEST_INTERRUPT_PRIORITY: 中断最低优先级。
  • LIBRARY_LOWEST_INTERRUPT_PRIORITY: 系统可管理的最高中断优先级。
 (4)、创建任务Task,在 Tasks and Queues 进行配置。

 默认空闲任务是在系统无其它任务执行时执行。

 然后我们创建两个LED任务。

  • Task Name: 任务名称
  • Priority: 优先级,在 FreeRTOS 中,数值越大优先级越高,0 代表最低优先级
  • Stack Size (Words): 堆栈大小,单位为字,在32位处理器(STM32),一个字等于4字节,如果传入512那么任务大小为512*4字节
  • Entry Function: 入口函数
  • Code Generation Option: 代码生成选项
  • Parameter: 任务入口函数形参,不用的时候配置为0或NULL即可
  • Allocation: 分配方式:Dynamic 动态内存创建
  • Buffer Name: 缓冲区名称
  • Conrol Block Name: 控制块名称
(5)、输入工程名,选择工程路径(不要有中文),选择MDK-ARM V5;勾选Generated periphera initialization as a pair of ‘.c/.h’ files per IP ;点击GENERATE CODE,生成工程代码。

四、程序编程

生成的程序中的freertos.c文件下就可以看到STM32CubeMX已经创建了LED1和LED2两个任务的创建和调度,直接在任务中编写代码即可运行。以下为freertos.c原始文件:

void MX_FREERTOS_Init(void) {
 /* definition and creation of defaultTask */
  osThreadDef(defaultTask, StartDefaultTask, osPriorityNormal, 0, 128);
  defaultTaskHandle = osThreadCreate(osThread(defaultTask), NULL);

  /* definition and creation of LED1 */
  osThreadDef(LED1, LED1_Task02, osPriorityIdle, 0, 128);
  LED1Handle = osThreadCreate(osThread(LED1), NULL);

  /* definition and creation of LED2 */
  osThreadDef(LED2, LED2_Task03, osPriorityIdle, 0, 128);
  LED2Handle = osThreadCreate(osThread(LED2), NULL);

  /* USER CODE BEGIN RTOS_THREADS */
  /* add threads, ... */
  /* USER CODE END RTOS_THREADS */

}

/* USER CODE END Header_LED1_Task02 */
void LED1_Task02(void const * argument)
{
  /* USER CODE BEGIN LED1_Task02 */
  /* Infinite loop */
  for(;;)
  {
    osDelay(1);
  }
  /* USER CODE END LED1_Task02 */
}

/* USER CODE END Header_LED2_Task03 */
void LED2_Task03(void const * argument)
{
  /* USER CODE BEGIN LED2_Task03 */
  /* Infinite loop */
  for(;;)
  {
    osDelay(1);
  }
  /* USER CODE END LED2_Task03 */
}

该文件的顶端定义了任务的ID,任务的调用都和这个ID相关。

/* USER CODE END Variables */
osThreadId defaultTaskHandle;
osThreadId LED1Handle;
osThreadId LED2Handle;

如果用了time1作为时基,在main.c文件下可以看到调用了定时器中断回调函数,如果需要用其他定时器可以在这里添加定时器程序

(1)、任务创建 

任务创建的方式分为静态创建和动态创建,不同之处是利用不同的运行内存。

在创建之前需要引用osThreadDef定义一下任务

osThreadDef(name, thread, priority, instances, stacksz)
/// 创建一个具有函数、优先级和堆栈要求的线程定义。
/// \param         name         线程函数的名称。
/// \param         thread       任务函数名称。
/// \param         priority     线程函数的初始优先级。
/// \param         instances    可能的线程实例数。
/// \param         stacksz      线程函数的堆栈大小(以字节为单位)。

//例:
osThreadDef(LED1, LED1_Task02, osPriorityIdle, 0, 128);
//即创建了一个名为LED1的线程定义,调用函数为LED1_Task02。

使用动态/静态内存的方法创建一个任务。

函数osThreadCreate (const osThreadDef_t *thread_def, void *argument)
参数thread_def: 引用由osThreadDef定义的任务

argument: 任务入口函数形参
返回值成功返回任务ID,失败返回0

例:创建任务3(LED3_Task04)

//1,先宏定义任务ID
osThreadId LED3Handle;

//2、定义任务函数
void LED3_Task04(void const * argument);

//3、在void MX_FREERTOS_Init(void)中创建任务线程
void MX_FREERTOS_Init(void)
{
 //****省略*****//
//创建任务LED3
  osThreadDef(LED3, LED3_Task04, osPriorityIdle, 0, 128);
  LED3Handle = osThreadCreate(osThread(LED3), NULL);

}

//4、编写任务函数
void LED3_Task04(void const * argument)
{
  for(;;)
  {
	
		printf("this LED3 run \r\n");
        osDelay(800);
  }
}

//注,此任务仅作举例使用。
(2)、任务删除

删除任务。任务被删除后就不复存在,也不会再进入运行态。

函数 osThreadTerminate (osThreadId thread_id)
参数thread_id: 被删除任务的ID
返回值错误码(osStatus)

 例:

osThreadTerminate (LED1Handle); //彻底删除任务LED1Handle
(3)、任务延时函数
(1).osDelay

相对延时函数。用于阻塞延时,调用该函数后,任务将进入阻塞状态,进入阻塞态的任务将让出 CPU 资源。

函数osStatus osDelay (uint32_t millisec)
参数millisec: 毫秒数
返回值错误码

要想使用该函数必须在 Include parameters 中把 vTaskDelay 选择 Enabled 来使能。

例:

void vTaskA( void * pvParameters )
{
    while (1) 
    {
        // ...
        // 这里为任务主体代码
        // ...

        /* 调用相对延时函数,阻塞 1000 个 tick */ 
        osDelay( 1000 ); 
    }
}

(2)、osThreadTerminate

绝对延时函数。常用于较精确的周期运行任务,比如我有一个任务,希望它以固定频率定期执行,而不受外部的影响,任务从上一次运行开始到下一次运行开始的时间间隔是绝对的,而不是相对的。

函数osStatus osDelayUntil (uint32_t *PreviousWakeTime, uint32_t millisec)
参数PreviousWakeTime: 任务上一次离开阻塞态(被唤醒)的时刻。这个时刻被用作一个参考点来计算该任务下一次离开阻塞态的时刻

millisec: 毫秒数
返回值错误码

 要想使用该函数必须在 Include parameters 中把 vTaskDelayUntil 选择 Enabled 来使能。

例:

//注意:在使用的时候要将延时时间转化为系统节拍,在任务主体之前要调用延时函数。
void vTaskA( void * pvParameters ) 
{
    /* 用于保存上次时间。调用后系统自动更新 */ 
    static portTickType PreviousWakeTime; 
    /* 设置延时时间,将时间转为节拍数 */ 
    const portTickType TimeIncrement = pdMS_TO_TICKS(1000); 

    /* 获取当前系统时间 */ 
    PreviousWakeTime = osKernelSysTick();

    while (1)
    {
        /* 调用绝对延时函数,任务时间间隔为 1000 个 tick */ 
        osDelayUntil( &PreviousWakeTime,TimeIncrement ); 
 
        // ...
        // 这里为任务主体代码 
        // ...
 
    }
}

 在生成的两个LED任务中编写运行函数程序。任务1计数到10 时删除任务2

//任务LED1
void LED1_Task02(void const * argument)
{
  /* USER CODE BEGIN LED1_Task02 */
  /* Infinite loop */
	int i=0;
	
  for(;;)
  {
		HAL_GPIO_TogglePin(GPIOE,GPIO_PIN_5);   //LED1状态每500s翻转一次
		printf("this LED1 run %d\r\n",i);
		i++;
		if(i==10)
		{
			if(osThreadTerminate (LED2Handle)==0); //彻底删除任务LED2Handle
				printf("删除 LED2Handle\r\n");
		}
		

    osDelay(1000);
  }
  /* USER CODE END LED1_Task02 */
}

//任务LED2
void LED2_Task03(void const * argument)
{
  /* USER CODE BEGIN LED2_Task03 */
  /* Infinite loop */
  for(;;)
  {
		HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_5);   //LED1状态每500s翻转一次
		printf("this LED2 run \r\n");
    osDelay(600);
  }
  /* USER CODE END LED2_Task03 */
}

五、下载验证

编译无误后下载到板子上,可以看到三个任务都能够正常运行,当任务1计数到10 时将任务2删除了之后,任务2不再继续运行

 六、参考文献

STM32CubeMX学习笔记(28)——FreeRTOS实时操作系统使用(任务管理)_stm32 rtos 任务固定时间运行-CSDN博客

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/477871.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

LabVIEW柴油机安保监控系统

LabVIEW柴油机安保监控系统 随着航运业的快速发展,确保船舶柴油机的安全稳定运行变得尤为重要。船舶柴油机故障不仅会导致重大的经济损失,还可能危及人员安全和环境。设计并开发了一套基于LabVIEW平台的柴油机安保监控系统,旨在通过实时监控…

VMware 虚拟机安装 CentOS Stream 9【图文详细教程】

系统需要开启虚拟化 VMware Pro 17 安装:https://www.yuque.com/u27599042/ccv8wh/ztmn0vkg3iimqyed CentOS Stream 9 镜像下载 https://www.centos.org/centos-stream/根据你电脑的操作系统类型,选择点击下载 创建虚拟机 在 VMware 中,…

JavaScript 权威指南第七版(GPT 重译)(六)

第十五章:JavaScript 在 Web 浏览器中 JavaScript 语言是在 1994 年创建的,旨在使 Web 浏览器显示的文档具有动态行为。自那时以来,该语言已经发生了显著的演变,与此同时,Web 平台的范围和功能也迅速增长。今天&#…

ES 8.x的多实例集群搭建与角色规划

ES 8 多实例集群搭建与角色规划 ES 8版本与之前版本存在较大改变,第一个区别就是启动时默认开启了安全模式,也就是即便是测试环境也需要用户名密码和https传输层安全证书。此外,集群节点的角色也与之前不同,除了新增角色外在配置…

设计技能UP UP UP!揭秘电路仿真软件高级技巧,让您成为设计大神

电路设计是一门技术活,而精湛的技巧往往决定了设计的成败。今天,让我们一起探讨电路仿真软件的高级技巧,助您在设计领域中实现技能的飙升! 电路仿真软件的高级功能解析 电路仿真软件不仅仅是简单的模拟工具,它还具备许…

Superstate 创始人 Robert Leshner 确认出席 Hack.Summit() 2024 区块链开发者大会

随着Web3技术的日新月异,一场备受瞩目的区块链盛会——Hack.Summit() 2024区块链开发者大会,即将于2024年4月9日至10日在香港数码港璀璨启幕。此次大会不仅是Hack.Summit()系列在亚洲的首度亮相,更象征着区块链行业对亚洲,特别是香…

txt、pdf等文件转为一行一行的doccano数据集输入格式

文章目录 doccano 数据集导入简介代码实现代码运行结果代码公开 doccano 数据集导入 在Doccano 导入数据集时,使用TextLine的文件格式,导入的文件需要为一行一行文本的数据格式,每一行文本在导入Doccano后就是一条数据。 简介 主要工作说明…

AndroidStudio相关

1、AndroidStudio 改变.gradle .AndroidStudio 默认存储位置 1、AndroidStudio安装路径下 -> bin -> idea.properties 文件 将1和2的路径设置为自己的路径,将3和4取消注释或复制在下方 注意: idea.plugins.path 和 idea.log.path 必须去掉注释或重…

Etcd Raft 协议(进阶篇)

前言 在正式开始介绍 Raft 协议之间,我们有必要简单介绍一下其相关概念。在分布式系统中,一致性是比较常见的概念,所谓一致性指的是集群中的多个节点在状态上达成一致。在程序和操作系统不会崩溃、硬件不会损坏、服务器不会掉电、网络绝对可靠…

基于SpringBoot精品在线试题库系统

采用技术 基于SpringBoot精品在线试题库系统的设计与实现~ 开发语言:Java 数据库:MySQL 技术:SpringBootMyBatis 工具:IDEA/Ecilpse、Navicat、Maven 页面展示效果 系统功能结构图 学生管理 教师管理 专业管理 试卷管理 …

MySQL索引优化,MySQL索引失效的场景(超详细!)

目录 1.不遵循最左前缀原则 2.在索引列上使用(计算,函数,or,类型转换),都是导致失效而导致全表扫描 3.范围查询 4.Select * 导致回表查询 5.不等空值还有or导致索引失效 6.like %写在左边导致索引失…

计算机408网课评测+资料分享

408当然有比较好的网课推荐,比如王道的视频课 现在大部分人备战408基本都用王道的讲义,然后再搭配王道408的课程来听,可以学的很好。 其中408视频课中,我认为讲的比较好的是数据结构,和操作系统,计算机组…

Talk|Mila研究所蒙特利尔大学刘圳:三维表征和三维网格的重建与生成

本期为TechBeat人工智能社区第580期线上Talk。 北京时间3月21日(周四)20:00,Mila研究所&蒙特利尔大学博士生—刘圳的Talk已经准时在TechBeat人工智能社区开播! 他与大家分享的主题是: “三维表征和三维网格的重建与生成”,向大家系统地介…

基于Java的校园疫情防控管理系统(Vue.js+SpringBoot)

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 学生2.2 老师2.3 学校管理部门 三、系统展示四、核心代码4.1 新增健康情况上报4.2 查询健康咨询4.3 新增离返校申请4.4 查询防疫物资4.5 查询防控宣传数据 五、免责说明 一、摘要 1.1 项目介绍 基于JAVAVueSpringBoot…

收割互联网大厂Offer面经

大家好,我是爱编程的喵喵。双985硕士毕业,现担任全栈工程师一职,热衷于将数据思维应用到工作与生活中。从事机器学习以及相关的前后端开发工作。曾在阿里云、科大讯飞、CCF等比赛获得多次Top名次。现为CSDN博客专家、人工智能领域优质创作者。…

paddlepaddle框架构建数据集进行分类问题的时候,会发现数据集在构建的过程中不会构建标签(花分类)

问题描述 在做一个paddlepaddle项目的时候,需要使用神经网络对他进行分类,数据集的结构如下图,这时候我们可以使用常用dataset方法对数据集进行构建。 这时候我们就会发现一个问题,就是这个矿建不是构建标签,也就是说…

vue在页面使用Vue.prototype全局变量

文章目录 Vue.prototype 的基本概念使用 Vue.prototype 添加全局属性和方法添加全局属性添加全局方法 使用场景注意事项在模板中使用全局变量和方法方法1方法2方法3 Vue.prototype 的基本概念 Vue.prototype 是 Vue 实例的原型对象。在 JavaScript 中,每个构造函数…

【Java】Java程序员必备的一些流程图

一、spring的生命周期 Spring作为当前Java最流行、最强大的轻量级容器框架,了解熟悉spring的生命周期非常有必要; 首先容器启动后,对bean进行初始化按照bean的定义,注入属性检测该对象是否实现了xxxAware接口,并将相…

openlayers 入门教程(四):layers 篇

还是大剑师兰特:曾是美国某知名大学计算机专业研究生,现为航空航海领域高级前端工程师;CSDN知名博主,GIS领域优质创作者,深耕openlayers、leaflet、mapbox、cesium,canvas,webgl,ech…

20240316-2-协同过滤(collaborative filtering)

协同过滤(collaborative filtering) 直观解释 协同过滤是推荐算法中最常用的算法之一,它根据user与item的交互,发现item之间的相关性,或者发现user之间的相关性,进行推荐。比如你有位朋友看电影的爱好跟你类似,然后最…