【FreeRTOS】FreeRTOS操作系统在嵌入式单片机上裸机移植

目录

一 RTOS概述

二 FreeRTOS移植

三 FreeRTOS使用

四 附录


一 RTOS概述

        

        先了解一些基础概念,以下内容摘自FreeRTOS官网(FreeRTOS™ - FreeRTOS™):

【1】RTOS基础知识

        实时操作系统 (RTOS) 是一种体积小巧、确定性强的计算机操作系统。 RTOS 通常用于需要在严格时间限制内对外部事件做出反应的嵌入式系统,如医疗设备和汽车电子控制单元 (ECU)。 通常,此类嵌入式系统中只有一两项功能需要确定性时序,即使嵌入式系统不需要严格的实时反应,使用 RTOS 仍能提供诸多优势。 

        RTOS 通常比通用操作系统体积更小、重量更轻,因此 RTOS 非常适用于 内存、计算和功率受限的设备。

【2】多任务处理

        内核是操作系统的核心组件。Linux 等通用操作系统采用的内核 允许多个用户看似同时访问计算机的处理器。这些用户可以各自执行多个程序,看起来像是并发运行。
每个执行的程序由操作系统控制下的一个或多个线程实现。如果操作系统能够以这种方式执行多个线程,则称为多任务处理。 像 FreeRTOS 这样的小型 RTOS 通常将线程称为任务,因为它们不支持虚拟内存,因此进程和线程之间没有区别。
使用多任务操作系统可以简化原本复杂的软件应用程序的设计:

  • 操作系统的多任务处理和任务间通信功能允许将复杂的应用程序 划分为一组更小且更易于管理的任务。
  • 这种划分可以简化软件测试,确保团队分工明确,并促进代码复用。
  • 复杂的时序和排序细节将由 RTOS 内核负责,从而减轻了应用程序代码的负担。

【3】多任务处理与并发

        常规单核处理器一次只能执行一个任务,但多任务操作系统可以快速切换任务, 使所有任务看起来像是同时在执行。下图展示了 三个任务相对于时间的执行模式。任务名称用不同颜色标示,并写在左侧。时间从左向右移动, 彩色线条显示在特定时间执行的任务。上方展示了所感知的并发执行模式, 下方展示了实际的多任务执行模式。

【4】调度

        调度器是内核中负责决定在特定时间应执行什么任务的部分。内核 可以在任务的生命周期内多次暂停并恢复该任务。 如果任务 B 取代任务 A 成为当前执行的任务 (即任务 A 暂停,任务 B 恢复),我们就可以称任务 A “换出”,任务 B “换入”。

        调度策略是调度器用来决定何时执行哪个任务的算法。在(非实时)多用户系统中, 调度策略通常会确保每个任务获得“公平”的处理器时间。实时嵌入式系统中使用的策略详见下文。

        只有当调度算法决定执行不同的任务时,任务才会换出。这种切换可能在当前 执行的任务不知情的情况下发生,例如调度算法响应外部事件或定时器到期时; 还可能 发生在执行任务显式调用某个导致其让出、休眠(也称为延迟)或阻塞的 API 函数时。

        如果某任务让出,调度算法可能会再次选择同一任务执行。如果某任务休眠, 则在指定的延迟时间到期前不可被选择。 同样,如果某任务阻塞, 则在特定事件发生(例如,数据到达 UART)或超时期满之前将不可被选择。

        操作系统内核负责管理这些任务状态和转换, 确保根据调度算法和每个任务的当前状态在给定时间选择适当的任务执行。

        参考上图中的数字标记:

  • 在标记 (1) 处,任务 1 正在执行。
  • 在标记 (2) 处,内核将任务 1 换出……
  • ……并在标记 (3) 处将任务 2 换入。
  • 在任务 2 执行期间,在标记 (4) 处,任务 2 锁定了处理器外设以进行独占访问(图中不可见)。
  • 在标记 (5) 处,内核将任务 2 换出……
  • ……并在标记 (6) 处将任务 3 换入。
  • 任务 3 试图访问之前被任务 2 锁定的处理器外设,发现其被锁定,在标记 (7) 处阻塞以等待外设解锁。
  • 在标记 (8) 处,内核将任务 1 换入。
  • 如此往复。
  • 在标记 (9) 处,任务 2 再次执行,完成对外设的操作并解锁。
  • 在标记 (10) 处,任务 3 再次执行,发现外设可用,继续执行直到再次被换出。

【5】实时调度

        实时操作系统 (RTOS) 利用与通用(非实时)系统相同的原理来实现多任务处理, 但两者的目标截然不同。这一差异主要体现在调度策略上。实时嵌入式系统 旨在对现实世界的事件作出及时响应。这些事件通常有截止时间, 实时嵌入式系统必须在此之前响应,RTOS 调度策略必须确保遵守这些截止时间要求。

        为在小型 RTOS(如 FreeRTOS)中实现这一目标,软件工程师必须为每个任务分配优先级。RTOS 的调度策略 就是确保能够执行的最高优先级任务获得处理时间。如果存在多个能够运行的同等最高优先级任务(既没有延迟也没有阻塞),则调度策略可以选择在这些任务之间“公平”地分配处理时间。

        这种基本形式的实时调度并非万能,无法改变时间的快慢,应用程序编写者必须确保设定的时序约束在所选任务优先级安排下是可行的。

        以下为最基本的示例,涉及一个带有键盘、LCD 和控制算法的实时系统。

        用户每次按键后, 必须在合理的时间内获得视觉反馈,如果用户在此期间无法看到按键已被接受, 则该软件产品的使用感会很差(软实时)。如果最长可接受的响应时间是 100 毫秒,则任何介于 0 和 100 毫秒之间的响应都是 可接受的。此功能可作为自主任务实现,结构如下:

void vKeyHandlerTask( void *pvParameters )
{// Key handling is a continuous process and as such the task// is implemented using an infinite loop (as most real-time// tasks are).for( ;; ){[Block to wait for a key press event][Process the key press]}
}

        现在假设实时系统还在执行依赖于数字滤波输入的控制功能。输入必须 每 2 毫秒采样一次、滤波一次并执行控制周期。为了正确操作滤波器,采样时间 必须精确到 0.5 毫秒。此功能可作为自主任务实现,结构如下:

void vControlTask( void *pvParameters )
{for( ;; ){[Delay waiting for 2ms since the start of the previous cycle][Sample the input][Filter the sampled input][Perform control algorithm][Output result]}
}

软件工程师必须为控制任务分配最高优先级,因为:

  1. 控制任务的截止时间比按键处理任务更严格。
  2. 错过截止时间对控制任务的后果比对按键处理任务更严重。

下图演示了实时操作系统如何调度这些任务。RTOS 会自行创建一个任务,即空闲任务, 仅当没有其他任务能够执行时,该任务才会执行。RTOS 空闲任务总是处于 可以执行的状态。

        请参阅上图:

  • 起初,两个任务都不能运行:vControlTask 等待合适的时间来开始新的控制周期, 而 vKeyHandlerTask 则在等待按键操作。处理器时间分配给了 RTOS 空闲任务。
  • 在时间 t1 处,发生按键事件。vKeyHandlerTask 可以执行,其优先级高于 RTOS 空闲任务, 因此获得了处理器时间。
  • 在时间 t2 处,vKeyHandlerTask 已完成按键处理并更新 LCD。在按下另一个键之前该任务无法继续执行, 因此将自己挂起,RTOS 空闲任务恢复执行。
  • 在时间 t3 处,定时器事件指示执行下一个控制循环的时间到了。vControlTask 现在可以执行, 因为优先级最高的任务被立刻分配了处理器时间。
  • 在时间 t3 和 t4 之间,vControlTask 仍在执行时,发生了按键事件。vKeyHandlerTask 可以执行, 但由于其优先级低于 vControlTask,因此未获得任何处理器时间。
  • 在 t4 处, vControlTask 完成了控制周期的处理,并且直到下一次定时事件的发生前不能重新开始运行, 进入阻塞态。vKeyHandlerTask 现在成为可以运行的最高优先级的任务, 因此获得处理器时间以处理先前的按键事件。
  • 在 t5 处,按键事件处理完成,并且 vKeyHandlerTask 进入阻塞态等待下一次按键事件。再一次, 两个任务都未进入就绪态,RTOS 空闲任务获得处理器时间。
  • 在 t5 与 t6 之间,定时事件发生并处理,没有进一步的按键事件发生。
  • 在 t6 处发生按键事件,但在 vKeyHandlerTask 完成按键处理之前,发生了定时事件。 此时两个任务都可以执行。由于 vControlTask 具有更高的优先级, 因此 vKeyHandlerTask 在完成按键操作之前被挂起,vControlTask 获得处理器时间。
  • 在 t8 处,vControlTask 完成控制周期的处理,然后进入阻塞态等待下一次事件。vKeyHandlerTask 再次 成为运行的最高优先级任务,因此获得处理器时间,以便完成按键处理 。

二 FreeRTOS移植

        本文使用RTOS中应用最为广泛,且开源免费的FreeRTOS进行移植。

【1】下载FreeRTOS

        官网下载(下载 FreeRTOS - FreeRTOS™),我这边选择 FreeRTOSv202406.01-LTS 版本

【2】添加工程

        (1)解压缩下载下来的包,这边的移植只需要用到内核文件夹,所以取文件夹中 FreeRTOS-LTS\FreeRTOS\FreeRTOS-Kernel 路径,将文件夹 FreeRTOS-Kernel 整体拖出来备用:

        (2)Keil工程中添加 FreeRTOS/srcFreeRTOS/port  、 FreeRTOS/config 三个group,分别将下面的文件加入到工程中,其中:

  • FreeRTOS/src 中文件最外层根目录下;
  • FreeRTOS/port  中文件 port.c 在 FreeRTOS\portable\RVDS\ARM_CM0\  中(根据自己移植芯片的内核选择)、heap_4.c 在 FreeRTOS\portable\MemMang\ 中(根据选用的内存管理方式进行选择,详情可以参考https://zhuanlan.zhihu.com/p/12892844067);
  • FreeRTOS/config 中文件 FreeRTOSConfig.h 在例程examples中获取或者自己新建一个均可,我这边采用自己新建的方式,包含详细注释,可以参考下文;

        头文件的包含如下:

【3】文件修改

        主要针对配置文件 FreeRTOSConfig.h 进行修改,包含芯片支持库头文件 #include "hc32f072.h" 以及CPU主频定义 extern uint32_t SystemCoreClock;

        其他宏定义可以根据自己的工程实现进行裁剪:

#ifndef FREERTOS_CONFIG_H
#define FREERTOS_CONFIG_H#include "hc32f072.h"
extern uint32_t SystemCoreClock;#define configUSE_PREEMPTION                1//抢占式调度器开关
#define configUSE_IDLE_HOOK                 0//空闲任务钩子函数
#define configUSE_TICK_HOOK                 0//时间片钩子函数
#define configCPU_CLOCK_HZ                  ( SystemCoreClock )//CPU主频
#define configTICK_RATE_HZ                  ( ( TickType_t ) 1000 )//RTOS节拍频率 即1秒的中断次数
#define configMAX_PRIORITIES                ( 5 )//可使用的最大优先级
#define configMINIMAL_STACK_SIZE            ( ( unsigned short ) 60 )//定义空闲任务使用的堆栈大小
#define configTOTAL_HEAP_SIZE               ( ( size_t ) ( 8*1024 ) )//RTOS内核总计可用的有效的RAM大小
#define configMAX_TASK_NAME_LEN             ( 12 )//任务名最大长度
#define configUSE_TRACE_FACILITY            1//是否启动可视化跟踪调试
#define configUSE_16_BIT_TICKS              0//系统节拍计数器的变量类型  0为32位 1为16为
#define configIDLE_SHOULD_YIELD             1//空闲任务放弃CPU使用权给其他同优先级的用户任务
#define configUSE_MUTEXES                   1//是否使用互斥信号量
#define configQUEUE_REGISTRY_SIZE           8//设置可以注册的信号量和消息队列个数
#define configCHECK_FOR_STACK_OVERFLOW      0//堆栈溢出检查 大于0有效
#define configUSE_RECURSIVE_MUTEXES         1//是否使用递归互斥信号量
#define configUSE_MALLOC_FAILED_HOOK        0//内存申请失败钩子函数
#define configUSE_APPLICATION_TASK_TAG      0//为任务分配标签
#define configUSE_COUNTING_SEMAPHORES       1//是否使用计数信号量
#define configGENERATE_RUN_TIME_STATS       0//是否启用运行时间统计功能/* Co-routine definitions. */
#define configUSE_CO_ROUTINES               0//启用协程,启用协程以后必须添加文件croutine.c
#define configMAX_CO_ROUTINE_PRIORITIES     ( 2 )//协程的有效优先级数目/* Software timer definitions. */
#define configUSE_TIMERS                    0//是否启用软件定时器
#define configTIMER_TASK_PRIORITY           ( 2 )//软件定时器优先级
#define configTIMER_QUEUE_LENGTH            ( 5 )//软件定时器队列长度
#define configTIMER_TASK_STACK_DEPTH        ( 80 )//软件定时器任务堆栈大小/* Set the following definitions to 1 to include the API function, or zero
to exclude the API function. *///是否启用RTOS API函数
#define INCLUDE_vTaskPrioritySet            1
#define INCLUDE_uxTaskPriorityGet           1
#define INCLUDE_vTaskDelete                 1
#define INCLUDE_vTaskCleanUpResources       1
#define INCLUDE_vTaskSuspend                1
#define INCLUDE_vTaskDelayUntil             1
#define INCLUDE_vTaskDelay                  1/* Normal assert() semantics without relying on the provision of an assert.h
header file. */
#define configASSERT( x ) if( ( x ) == 0 )  { taskDISABLE_INTERRUPTS(); for( ;; ); }/* Definitions that map the FreeRTOS port interrupt handlers to their CMSIS
standard names - or at least those used in the unmodified vector table. */
#define vPortSVCHandler SVC_Handler
#define xPortPendSVHandler PendSV_Handler
#define xPortSysTickHandler SysTick_Handler#endif /* FREERTOS_CONFIG_H */

        至此移植完成。

三 FreeRTOS使用

        (1)实现任务创建函数 FreeRTOS_TaskCreate

void STA_Task(void* param);
void IO_Task(void* param);
void TMP_Task(void* param);static TaskHandle_t AppStaTask_handle = NULL;
static TaskHandle_t AppIOTask_handle = NULL;
static TaskHandle_t AppTmpTask_handle = NULL;#define STA_TASK_SIZE 128
#define IO_TASK_SIZE 512
#define TMP_TASK_SIZE 128int FreeRTOS_TaskCreate(void)
{BaseType_t xReturn1 = pdPASS;BaseType_t xReturn2 = pdPASS;//创建心跳灯任务xReturn1 = xTaskCreate((TaskFunction_t )STA_Task,       //任务入口函数(const char*    )"StaTask",         //任务名字(uint16_t       )STA_TASK_SIZE,     //任务栈大小(void*          )NULL,              //任务入口函数参数(UBaseType_t    )2,                 //任务的优先级(TaskHandle_t*  )&AppStaTask_handle);//任务控制块指针//创建输入输出任务xReturn2 = xTaskCreate((TaskFunction_t )IO_Task,        //任务入口函数(const char*    )"IOTask",          //任务名字(uint16_t       )IO_TASK_SIZE,      //任务栈大小(void*          )NULL,              //任务入口函数参数(UBaseType_t    )3,                 //任务的优先级(TaskHandle_t*  )&AppIOTask_handle);//任务控制块指针//创建加热任务(按键蜂鸣加热温度采样)xReturn1 = xTaskCreate((TaskFunction_t )TMP_Task,       //任务入口函数(const char*    )"TmpTask",         //任务名字(uint16_t       )TMP_TASK_SIZE,     //任务栈大小(void*          )NULL,              //任务入口函数参数(UBaseType_t    )4,                 //任务的优先级(TaskHandle_t*  )&AppTmpTask_handle);//任务控制块指针if (pdPASS != xReturn1 || pdPASS != xReturn2){return -1;  // 任务创建失败}vTaskStartScheduler();//启动调度return 0;
}

        

        (2)实现三个任务入口函数 STA_Task IO_Task TMP_Task

void STA_Task(void* param)
{while(1){// 计算栈使用总量和百分比UBaseType_t uxUsedStackSize = STA_TASK_SIZE - uxTaskGetStackHighWaterMark(NULL);UBaseType_t uxUsagePercentage = (uxUsedStackSize * 100) / STA_TASK_SIZE;// 打印栈使用总量和百分比Dbg_Printf("STA_Task-->Stack Used: %u/%u (%u%%)\r\n", (unsigned int)uxUsedStackSize, (unsigned int)STA_TASK_SIZE, (unsigned int)uxUsagePercentage);vTaskDelay(1000);}
}void IO_Task(void* param)
{while(1){// 计算栈使用总量和百分比UBaseType_t uxUsedStackSize = IO_TASK_SIZE - uxTaskGetStackHighWaterMark(NULL);UBaseType_t uxUsagePercentage = (uxUsedStackSize * 100) / IO_TASK_SIZE;// 打印栈使用总量和百分比Dbg_Printf("IO_Task-->Stack Used: %u/%u (%u%%)\r\n", (unsigned int)uxUsedStackSize, (unsigned int)IO_TASK_SIZE, (unsigned int)uxUsagePercentage);vTaskDelay(1000);}
}void TMP_Task(void* param)
{while(1){// 计算栈使用总量和百分比UBaseType_t uxUsedStackSize = TMP_TASK_SIZE - uxTaskGetStackHighWaterMark(NULL);UBaseType_t uxUsagePercentage = (uxUsedStackSize * 100) / TMP_TASK_SIZE;// 打印栈使用总量和百分比Dbg_Printf("TMP_Task-->Stack Used: %u/%u (%u%%)\r\n", (unsigned int)uxUsedStackSize, (unsigned int)TMP_TASK_SIZE, (unsigned int)uxUsagePercentage);vTaskDelay(1000);}
}

        

        (3)主函数包含头文件 FreeRTOS.h 、task.h ,创建任务 FreeRTOS_TaskCreate 并启动:

#include "ddl.h"
#include "Dbg.h"#include "FreeRTOS.h"
#include "task.h"static void App_SysClkInit(void)
{stc_sysctrl_clk_cfg_t stcCfg;stc_sysctrl_pll_cfg_t stcPLLCfg;Sysctrl_SetPeripheralGate(SysctrlPeripheralFlash, TRUE);    ///< 使能FLASH模块的外设时钟Flash_WaitCycle(FlashWaitCycle1);Sysctrl_SetRCHTrim(SysctrlRchFreq4MHz);             ///< PLL使用RCH作为时钟源,因此需要先设置RCHstcPLLCfg.enInFreq    = SysctrlPllInFreq4_6MHz;     ///< RCH 4MHzstcPLLCfg.enOutFreq   = SysctrlPllOutFreq36_48MHz;  ///< PLL 输出48MHzstcPLLCfg.enPllClkSrc = SysctrlPllRch;              ///< 输入时钟源选择RCHstcPLLCfg.enPllMul    = SysctrlPllMul12;            ///< 4MHz x 12 = 48MHzSysctrl_SetPLLFreq(&stcPLLCfg);///< 选择PLL作为HCLK时钟源;stcCfg.enClkSrc  = SysctrlClkPLL;///< HCLK SYSCLK/2stcCfg.enHClkDiv = SysctrlHclkDiv1;///< PCLK 为HCLK/8stcCfg.enPClkDiv = SysctrlPclkDiv1;///< 系统时钟初始化Sysctrl_ClkInit(&stcCfg);//    ///< 使用外部晶振
//    Sysctrl_SetXTHFreq(SysctrlXthFreq8_16MHz);
//    Sysctrl_XTHDriverCfg(SysctrlXtalDriver1);
//    Sysctrl_SetXTHStableTime(SysctrlXthStableCycle16384);
//    Sysctrl_ClkSourceEnable(SysctrlClkXTH, TRUE);
//    delay1ms(10);//    ///< 时钟切换
//    Sysctrl_SysClkSwitch(SysctrlClkXTH);
}int32_t main(void)
{//系统时钟App_SysClkInit();//打印初始化(调试用)Dbg_Init();Dbg_Cfg();//创建任务并启动FreeRTOS_TaskCreate();while(1);
}

        (4)实现任务切换效果如下:

四 附录

 【1】FreeRTOS官网:FreeRTOS™ - FreeRTOS™

【2】掌握 FreeRTOS 实时内核:https://freertoskernel.asicfans.com/

【3】野火开发板的RTOS学习:[野火]STM32MP1-FreeRTOS应用开发实战指南——基于STM32MP157开发板 — [野火]STM32MP1-FreeRTOS应用开发实战指南——基于STM32MP157开发板 文档

【4】FreeRTOS操作系统几种典型的进程间通信方式及使用示例:【FreeRTOS】FreeRTOS操作系统几种典型的进程间通信方式及使用示例-CSDN博客

【5】FreeRTOS学习:https://zhuanlan.zhihu.com/p/650595248

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

文件包含漏洞第一关

一、什么是文件包含漏洞 1.文件包含漏洞概述 和SQL注入等攻击方式一样&#xff0c;文件包含漏洞也是一种注入型漏洞&#xff0c;其本质就是输入一段用户能够控制的脚本或者代码&#xff0c;并让服务端执行。 什么叫包含呢&#xff1f;以PHP为例&#xff0c;我们常常把可重复使…

瑞芯微RK3576(1)-硬件设计

过年期间&#xff0c;趁着放假时间做了一款3576的核心板 方案是2G DDR432G emmc 引出所有IO口 关于接口方面&#xff0c;考虑了一段时间&#xff0c;最终决定使用BTB的模式&#xff0c;主要是能够出更多的IO&#xff0c;方便拆卸&#xff0c;最让我担心的是BTB的位置问题 为了…

Java 大视界 -- Java 大数据在智能医疗药品研发数据分析与决策支持中的应用(126)

&#x1f496;亲爱的朋友们&#xff0c;热烈欢迎来到 青云交的博客&#xff01;能与诸位在此相逢&#xff0c;我倍感荣幸。在这飞速更迭的时代&#xff0c;我们都渴望一方心灵净土&#xff0c;而 我的博客 正是这样温暖的所在。这里为你呈上趣味与实用兼具的知识&#xff0c;也…

JWT的学习

1、HTTP无状态及解决方案 HTTP一种是无状态的协议&#xff0c;每次请求都是一次独立的请求&#xff0c;一次交互之后就是陌生人。 以CSDN为例&#xff0c;先登录一次&#xff0c;然后浏览器退出&#xff0c;这个时候在进入CSDN&#xff0c;按理说服务器是不知道你已经登陆了&…

时序和延时

1、延迟模型的类型 verilog有三种类型的延迟模型&#xff1a;分布延迟 、 集总延迟 、 路径延迟&#xff08;pin to pin&#xff09; 1.1、 分布延迟 分布延迟是在每个独立元件的基础上进行定义的。 module M(output wire out ,input wire a …

SpringBoot基础Kafka示例

这里将生产者和消费者放在一个应用中 使用的Boot3.4.3 引入Kafka依赖 <dependency><groupId>org.springframework.kafka</groupId><artifactId>spring-kafka</artifactId> </dependency>yml配置 spring:application:name: kafka-1#kafka…

API调试工具的无解困境:白名单、动态IP与平台设计问题

引言 你是否曾经在开发中遇到过这样的尴尬情形&#xff1a;你打开了平台的API调试工具&#xff0c;准备一番操作&#xff0c;结果却发现根本无法连接到平台&#xff1f;别急&#xff0c;问题出在调试工具本身。今天我们要吐槽的就是那些神奇的开放平台API调试工具&#xff0c;…

多方安全计算(MPC)电子拍卖系统

目录 一、前言二、多方安全计算(MPC)与电子拍卖系统概述2.1 多方安全计算(MPC)的基本概念2.2 电子拍卖系统背景与需求三、MPC电子拍卖系统设计原理3.1 系统总体架构3.2 电子拍卖中的安全协议3.3 数学与算法证明四、数据加解密模块设计五、GPU加速与系统性能优化六、GUI设计与系…

【Linux篇】初识Linux指令(上篇)

Linux命令世界&#xff1a;从新手到高手的必备指南 一 Linux发展与历史1.1 Linux起源与发展1.2 Linux与Windows操作系统对比 二 Linux常用操作指令2.1 ls命令 - “List”&#xff08;列出文件)2.2 pwd指令- "打印当前工作目录"2.3 cd指令 - “Change Directory”&…

编程视界:C++命名空间

目录 命名空间 为什么要使用命名空间 什么是命名空间 命名空间的使用方式 关键点总结 命名空间的嵌套使用 匿名命名空间 跨模块调用问题 命名空间可以多次定义 总结 首先从C的hello,world程序入手&#xff0c;来认识一下C语言 #include <iostream> using name…

Redux 和 MobX 高频面试题

Redux 和 MobX 是 React 生态中的两大状态管理方案&#xff0c;在面试中常涉及 原理、使用方式、对比、最佳实践 等方面。以下是 高频面试题 详细答案&#xff0c;助你轻松应对面试&#xff01;&#x1f680; &#x1f525; Redux 部分 1. Redux 是什么&#xff1f;为什么需要…

Excel 保护工作簿:它能解决哪些问题?如何正确使用?

在日常办公中&#xff0c;Excel 表格常常涉及多人协作、重要数据保护&#xff0c;甚至是避免误操作的情况。这时候&#xff0c;“保护工作簿”功能就能派上用场。它能有效防止他人修改表结构、删除工作表&#xff0c;甚至可以设置密码&#xff0c;确保数据的完整性和安全性。今…

Android Retrofit 框架注解定义与解析模块深度剖析(一)

一、引言 在现代 Android 和 Java 开发中&#xff0c;网络请求是不可或缺的一部分。Retrofit 作为 Square 公司开源的一款强大的类型安全的 HTTP 客户端&#xff0c;凭借其简洁易用的 API 和高效的性能&#xff0c;在开发者社区中广受欢迎。Retrofit 的核心特性之一便是通过注…

C# Enumerable类 之 数据分组

总目录 前言 在 C# 中&#xff0c;System.Linq.Enumerable 类是 LINQ&#xff08;Language Integrated Query&#xff09;的核心组成部分&#xff0c;它提供了一系列静态方法&#xff0c;用于操作实现了 IEnumerable 接口的集合。通过这些方法&#xff0c;我们可以轻松地对集合…

推理模型对SQL理解能力的评测:DeepSeek r1、GPT-4o、Kimi k1.5和Claude 3.7 Sonnet

引言 随着大型语言模型&#xff08;LLMs&#xff09;在技术领域的应用日益广泛&#xff0c;评估这些模型在特定技术任务上的能力变得越来越重要。本研究聚焦于四款领先的推理模型——DeepSeek r1、GPT-4o、Kimi k1.5和Claude 3.7 Sonnet在SQL理解与分析方面的能力&#xff0c;…

IDEA接入阿里云百炼中免费的通义千问[2025版]

安装deepseek 上一篇文章IDEA安装deepseek最新教程2025中说明了怎么用idea安装codeGPT插件&#xff0c;并接入DeepSeek&#xff0c;无奈接入的官方api已经不能使用了&#xff0c;所以我们尝试从其他地方接入 阿里云百炼https://bailian.console.aliyun.com/ 阿里云百炼‌是阿…

实施一套先进的智能摄像头服务系统。

一、项目背景 随着物联网、人工智能和大数据技术的飞速发展&#xff0c;智能摄像头已成为家庭、企业以及公共安全领域的重要设备。其便捷、高效、智能的特点&#xff0c;使得市场需求日益增长。为了满足用户对智能监控的多样化需求&#xff0c;提供更加全面、可靠的监控服务&a…

linux自启动服务

在Linux环境中&#xff0c;systemd是一个系统和服务管理器&#xff0c;它为每个服务使用.service文件进行配置。systemctl是用于控制系统服务的主要工具。本文将详细介绍如何使用systemctl来管理vsftpd服务&#xff0c;以及如何设置服务自启动。 使用Systemd设置自启动服务 创…

010-Catch2

Catch2 一、框架简介 Catch2 是一个基于 C 的现代化单元测试框架&#xff0c;支持 TDD&#xff08;测试驱动开发&#xff09;和 BDD&#xff08;行为驱动开发&#xff09;模式。其核心优势在于&#xff1a; 单头文件设计&#xff1a;v2.x 版本仅需包含 catch.hpp 即可使用自然…

数字人分身开发指南:从概念到实战

一、什么是数字人分身&#xff1f; 想象一下&#xff0c;在电脑或手机屏幕里&#xff0c;一个能跟你聊天、回答问题&#xff0c;甚至还能做表情的虚拟角色。这就是数字人分身&#xff0c;它用上了人工智能技术&#xff0c;让机器也能像人一样交流。无论是在线客服、网络主播还…