(学习日记)2024.03.25:UCOSIII第二十二节:系统启动流程详解

写在前面:
由于时间的不足与学习的碎片化,写博客变得有些奢侈。
但是对于记录学习(忘了以后能快速复习)的渴望一天天变得强烈。
既然如此
不如以天为单位,以时间为顺序,仅仅将博客当做一个知识学习的目录,记录笔者认为最通俗、最有帮助的资料,并尽量总结几句话指明本质,以便于日后搜索起来更加容易。


标题的结构如下:“类型”:“知识点”——“简短的解释”
部分内容由于保密协议无法上传。


点击此处进入学习日记的总目录

2024.03.25:UCOSIII第二十二节:系统启动流程详解

  • 三十六、UCOSIII:系统启动流程详解
    • 1、运行启动文件
    • 2、主流程 main
    • 3、系统初始化函数OSInit()
      • 1. 空闲任务的初始化
      • 2. 空闲任务的定义
      • 3. 时钟节拍任务的初始化
    • 4、启动任务AppTaskStart()
      • 1. 时间戳初始化
      • 2. SysTick初始化
      • 3. 内存初始化
    • 5、任务调度器启动函数OSStart()

三十六、UCOSIII:系统启动流程详解

本章总结一下系统在刚启动时初始化的过程
介绍顺序为上电后的运行顺序:

1、运行启动文件

在系统上电的时候第一个执行的是启动文件里面由汇编编写的复位函数Reset_Handler

Reset_Handler   PROC
EXPORT  Reset_Handler             [WEAK]
IMPORT  __main
IMPORT  SystemInit
LDRR0, =SystemInitBLX     R0
LDRR0, =__mainBX      R0
ENDP

复位函数的最后会调用C库函数__main__main函数的主要工作是初始化系统的堆和栈,最后调用C中的main()函数,从而去到C的世界。

2、主流程 main

首先看main函数:

int  main (void)
{OS_ERR  err;OSInit(&err);                                               /* Init uC/OS-III.                                      */OSTaskCreate((OS_TCB     *)&AppTaskStartTCB,                /* Create the start task                                */(CPU_CHAR   *)"App Task Start",(OS_TASK_PTR ) AppTaskStart,(void       *) 0,(OS_PRIO     ) APP_TASK_START_PRIO,(CPU_STK    *)&AppTaskStartStk[0],(CPU_STK_SIZE) APP_TASK_START_STK_SIZE / 10,(CPU_STK_SIZE) APP_TASK_START_STK_SIZE,(OS_MSG_QTY  ) 5u,(OS_TICK     ) 0u,(void       *) 0,(OS_OPT      )(OS_OPT_TASK_STK_CHK | OS_OPT_TASK_STK_CLR),(OS_ERR     *)&err);OSStart(&err);                                              /* Start multitasking (i.e. give control to uC/OS-III). */		
}

这种启动方式在野火的教程里被称作小心翼翼,十分谨慎法

这种方法是在main()函数中将硬件和RTOS系统先初始化好,然后创建一个启动任务后就启动调度器。
在启动任务里面创建各种应用任务,当所有任务都创建成功后,启动任务把自己删除,具体的伪代码实现如下:

int main (void)
{/* 硬件初始化 */HardWare_Init();		//(1)/* RTOS 系统初始化 */RTOS_Init();		//(2)/* 创建一个任务 */RTOS_TaskCreate(AppTaskCreate);		//(3)/* 启动RTOS,开始调度 */RTOS_Start();		//(4)
}/* 起始任务,在里面创建任务 */
voidAppTaskCreate( void *arg )		//(5)
{/* 创建任务1,然后执行 */RTOS_TaskCreate(Task1);		//(6)/* 当任务1阻塞时,继续创建任务2,然后执行 */RTOS_TaskCreate(Task2);/* ......继续创建各种任务 *//* 当任务创建完成,删除起始任务 */RTOS_TaskDelete(AppTaskCreate);		//(7)
}void Task1( void *arg )		//(8)
{while (1){/* 任务实体,必须有阻塞的情况出现 */}
}void Task2( void *arg )		//(9)
{while (1){/* 任务实体,必须有阻塞的情况出现 */}
}
  • (1):硬件初始化。来到硬件初始化这一步还属于裸机的范畴, 我们可以把需要使用到的硬件都初始化好而且测试好,确保无误。
  • (2):RTOS系统初始化。比如RTOS里面的全局变量的初始化, 空闲任务的创建等。不同的RTOS,它们的初始化有细微的差别。
  • (3):创建一个开始任务。然后在这个初始任务里面创建各种应用任务。
  • (4):启动RTOS调度器,开始任务调度。这个时候调度器就去执行刚刚创建好的初始任务。
  • (5):我们通常说任务是一个不带返回值的无限循环的C函数, 但是因为初始任务的特殊性,它不能是无限循环的,只执行一次后就关闭。在初始任务里面我们创建我们需要的各种任务。
  • (6):创建任务。每创建一个任务后它都将进入就绪态,系统会进行一次调度, 如果新创建的任务的优先级比初始任务的优先级高的话,那将去执行新创建的任务, 当新的任务阻塞时再回到初始任务被打断的地方继续执行。反之,则继续往下创建新的任务,直到所有任务创建完成。
  • (7):各种应用任务创建完成后,初始任务自己关闭自己,使命完成。
  • (8)(9):任务实体通常是一个不带返回值的无限循环的C函数,函数体必须有阻塞的情况出现, 不然任务(如果优先权恰好是最高)会一直在while循环里面执行,其他任务没有执行的机会。

3、系统初始化函数OSInit()

在调用创建任务函数之前,我们必须要对系统进行一次初始化。
而系统的初始化是根据我们配置宏定义进行初始化的, 有一些则是系统必要的初始化,如空闲任务,时钟节拍任务等。

下面我们来看看系统初始化的主要源码:

void  OSInit (OS_ERR  *p_err)
{CPU_STK      *p_stk;CPU_STK_SIZE  size;if (p_err == (OS_ERR *)0){OS_SAFETY_CRITICAL_EXCEPTION();return;}OSInitHook();   /*初始化钩子函数相关的代码*/OSIntNestingCtr= (OS_NESTING_CTR)0;     /*清除中断嵌套计数器*/OSRunning =  OS_STATE_OS_STOPPED;       /*未启动多任务处理*/OSSchedLockNestingCtr = (OS_NESTING_CTR)0;/*清除锁定计数器*/OSTCBCurPtr= (OS_TCB *)0;       /*将OS_TCB指针初始化为已知状态  */OSTCBHighRdyPtr = (OS_TCB *)0;OSPrioCur = (OS_PRIO)0;                 /*将优先级变量初始化为已知状态*/OSPrioHighRdy                   = (OS_PRIO)0;OSPrioSaved                     = (OS_PRIO)0;if (OSCfg_ISRStkSize > (CPU_STK_SIZE)0){p_stk = OSCfg_ISRStkBasePtr;        /*清除异常栈以进行栈检查*/if (p_stk != (CPU_STK *)0){size  = OSCfg_ISRStkSize;while (size > (CPU_STK_SIZE)0){size--;*p_stk = (CPU_STK)0;p_stk++;}}}OS_PrioInit();  /*初始化优先级位图表*/OS_RdyListInit();       /*初始化就绪列表*/OS_TaskInit(p_err);   /*初始化任务管理器*/if (*p_err != OS_ERR_NONE){return;}OS_IdleTaskInit(p_err);    /* 初始化空闲任务  */       if (*p_err != OS_ERR_NONE){return;}OS_TickTaskInit(p_err);   /* 初始化时钟节拍任务*/        if (*p_err != OS_ERR_NONE){return;}OSCfg_Init();
}

在这个系统初始化中,我们主要看两个地方

  • 一个是空闲任务的初始化
  • 一个是时钟节拍任务的初始化

这两个任务是必须存在的任务,否则系统无法正常运行。

1. 空闲任务的初始化

其实初始化就是创建一个空闲任务,空闲任务的相关信息由系统默认指定, 用户不能修改

void  OS_IdleTaskInit (OS_ERR  *p_err)
{
#ifdef OS_SAFETY_CRITICALif (p_err == (OS_ERR *)0){OS_SAFETY_CRITICAL_EXCEPTION();return;}
#endifOSIdleTaskCtr = (OS_IDLE_CTR)0;         //(1)
/* ---------------- CREATE THE IDLE TASK ---------------- */OSTaskCreate((OS_TCB     *)&OSIdleTaskTCB,(CPU_CHAR   *)((void *)"μC/OS-III Idle Task"),(OS_TASK_PTR)OS_IdleTask,(void       *)0,(OS_PRIO     )(OS_CFG_PRIO_MAX - 1u),(CPU_STK    *)OSCfg_IdleTaskStkBasePtr,(CPU_STK_SIZE)OSCfg_IdleTaskStkLimit,(CPU_STK_SIZE)OSCfg_IdleTaskStkSize,(OS_MSG_QTY  )0u,(OS_TICK     )0u,(void       *)0,(OS_OPT)(OS_OPT_TASK_STK_CHK | OS_OPT_TASK_STK_CLR |OS_OPT_TASK_NO_TLS),(OS_ERR     *)p_err);               //(2)
}
  • (1):OSIdleTaskCtr在os.h头文件中定义,是一个32位无符号整型变量, 该变量的作用是用于统计空闲任务的运行的,怎么统计呢,我们在空闲任务中讲解。现在初始化空闲任务,系统就将OSIdleTaskCtr清零。
  • (2):我们可以很容易看到系统只是调用了OSTaskCreate()函数来创建一个任务,这个任务就是空闲任务, 任务优先级为OS_CFG_PRIO_MAX-1,OS_CFG_PRIO_MAX是一个宏,该宏定义表示μC/OS的任务优先级数值的最大值,我们知道, 在μC/OS系统中,任务的优先级数值越大,表示任务的优先级越低,所以空闲任务的优先级是最低的。 空闲任务栈大小为OSCfg_IdleTaskStkSize,它也是一个宏,在os_cfg_app.c文件中定义,默认为128, 则空闲任务栈默认为128*4=512字节。

2. 空闲任务的定义

空闲任务其实就是一个函数,其函数入口是OS_IdleTask

void  OS_IdleTask (void  *p_arg)
{CPU_SR_ALLOC();/* Prevent compiler warning for not using 'p_arg'*/p_arg = p_arg;while (DEF_ON){CPU_CRITICAL_ENTER();OSIdleTaskCtr++;
#if OS_CFG_STAT_TASK_EN > 0uOSStatTaskCtr++;
#endifCPU_CRITICAL_EXIT();/* Call user definable HOOK */OSIdleTaskHook();}
}

空闲任务的作用还是很大的,它是一个无限的死循环。
因为其优先级是最低的,所以任何优先级比它高的任务都能抢占它从而取得CPU的使用权。

为什么系统要空闲任务呢?
因为CPU是不会停下来的,即使啥也不干,CPU也不会停下来,此时系统就必须保证有一个随时处于就绪态的任务, 而且这个任务不会抢占其他任务,当且仅当系统的其他任务处于阻塞中,系统才会运行空闲任务。
这个任务可以做很多事情,任务统计, 钩入用户自定义的钩子函数实现用户自定义的功能等,但是需要注意的是,在钩子函数中用户不允许调用任何可以使空闲任务阻塞的函数接口, 空闲任务是不允许被阻塞的。

3. 时钟节拍任务的初始化

OS_TickTaskInit()函数也是创建一个时钟节拍任务

void  OS_TickTaskInit (OS_ERR  *p_err)
{
#ifdef OS_SAFETY_CRITICALif (p_err == (OS_ERR *)0){OS_SAFETY_CRITICAL_EXCEPTION();return;}
#endifOSTickCtr         = (OS_TICK)0u; /* Clear the tick counter   */OSTickTaskTimeMax = (CPU_TS)0u;OS_TickListInit();/* Initialize the tick list data structures  *//* ---------------- CREATE THE TICK TASK ---------------- */if (OSCfg_TickTaskStkBasePtr == (CPU_STK *)0){*p_err = OS_ERR_TICK_STK_INVALID;return;}if (OSCfg_TickTaskStkSize < OSCfg_StkSizeMin){*p_err = OS_ERR_TICK_STK_SIZE_INVALID;return;}/* Only one task at the 'Idle Task' priority              */if (OSCfg_TickTaskPrio >= (OS_CFG_PRIO_MAX - 1u)){*p_err = OS_ERR_TICK_PRIO_INVALID;return;}OSTaskCreate((OS_TCB     *)&OSTickTaskTCB,(CPU_CHAR   *)((void *)"μC/OS-III Tick Task"),(OS_TASK_PTR )OS_TickTask,(void       *)0,(OS_PRIO     )OSCfg_TickTaskPrio,(CPU_STK    *)OSCfg_TickTaskStkBasePtr,(CPU_STK_SIZE)OSCfg_TickTaskStkLimit,(CPU_STK_SIZE)OSCfg_TickTaskStkSize,(OS_MSG_QTY  )0u,(OS_TICK     )0u,(void       *)0,(OS_OPT)(OS_OPT_TASK_STK_CHK | OS_OPT_TASK_STK_CLR | OS_OPT_TASK_NO_TLS),(OS_ERR     *)p_err);
}

4、启动任务AppTaskStart()

系统在启动任务里面创建各种应用任务,当所有任务都创建成功后,启动任务把自己删除

static  void  AppTaskStart (void *p_arg)
{CPU_INT32U  cpu_clk_freq;CPU_INT32U  cnts;OS_ERR      err;(void)p_arg;BSP_Init();                                                 /* Initialize BSP functions                             */CPU_Init();cpu_clk_freq = BSP_CPU_ClkFreq();                           /* Determine SysTick reference freq.                    */cnts = cpu_clk_freq / (CPU_INT32U)OSCfg_TickRate_Hz;        /* Determine nbr SysTick increments                     */OS_CPU_SysTickInit(cnts);                                   /* Init uC/OS periodic time src (SysTick).              */Mem_Init();                                                 /* Initialize Memory Management Module                  */#if OS_CFG_STAT_TASK_EN > 0uOSStatTaskCPUUsageInit(&err);                               /* Compute CPU capacity with no task running            */
#endifCPU_IntDisMeasMaxCurReset();OSTaskCreate((OS_TCB     *)&AppTaskLed1TCB,                /* Create the Led1 task                                */(CPU_CHAR   *)"App Task Led1",(OS_TASK_PTR ) AppTaskLed1,(void       *) 0,(OS_PRIO     ) APP_TASK_LED1_PRIO,(CPU_STK    *)&AppTaskLed1Stk[0],(CPU_STK_SIZE) APP_TASK_LED1_STK_SIZE / 10,(CPU_STK_SIZE) APP_TASK_LED1_STK_SIZE,(OS_MSG_QTY  ) 5u,(OS_TICK     ) 0u,(void       *) 0,(OS_OPT      )(OS_OPT_TASK_STK_CHK | OS_OPT_TASK_STK_CLR),(OS_ERR     *)&err);OSTaskCreate((OS_TCB     *)&AppTaskLed2TCB,                /* Create the Led2 task                                */(CPU_CHAR   *)"App Task Led2",(OS_TASK_PTR ) AppTaskLed2,(void       *) 0,(OS_PRIO     ) APP_TASK_LED2_PRIO,(CPU_STK    *)&AppTaskLed2Stk[0],(CPU_STK_SIZE) APP_TASK_LED2_STK_SIZE / 10,(CPU_STK_SIZE) APP_TASK_LED2_STK_SIZE,(OS_MSG_QTY  ) 5u,(OS_TICK     ) 0u,(void       *) 0,(OS_OPT      )(OS_OPT_TASK_STK_CHK | OS_OPT_TASK_STK_CLR),(OS_ERR     *)&err);OSTaskCreate((OS_TCB     *)&AppTaskLed3TCB,                /* Create the Led3 task                                */(CPU_CHAR   *)"App Task Led3",(OS_TASK_PTR ) AppTaskLed3,(void       *) 0,(OS_PRIO     ) APP_TASK_LED3_PRIO,(CPU_STK    *)&AppTaskLed3Stk[0],(CPU_STK_SIZE) APP_TASK_LED3_STK_SIZE / 10,(CPU_STK_SIZE) APP_TASK_LED3_STK_SIZE,(OS_MSG_QTY  ) 5u,(OS_TICK     ) 0u,(void       *) 0,(OS_OPT      )(OS_OPT_TASK_STK_CHK | OS_OPT_TASK_STK_CLR),(OS_ERR     *)&err);OSTaskDel ( & AppTaskStartTCB, & err );}

其中需要注意的如下:

CPU_Init();
//时间戳初始化OS_CPU_SysTickInit(cnts);
//MCU的内核定时器SysTick初始化Mem_Init();
//内存初始化

1. 时间戳初始化

在启动任务AppTaskStart()中,有一个CPU初始化函数,CPU初始化函数可以初始化时间戳

void  CPU_Init (void)
{
/* --------------------- INIT TS ---------------------- */
#if ((CPU_CFG_TS_EN     == DEF_ENABLED) || \(CPU_CFG_TS_TMR_EN == DEF_ENABLED))CPU_TS_Init();     /* 时间戳测量的初始化   */#endif
/* -------------- INIT INT DIS TIME MEAS -------------- */
#ifdef  CPU_CFG_INT_DIS_MEAS_ENCPU_IntDisMeasInit();  /* 最大关中断时间测量初始化     */#endif/* ------------------ INIT CPU NAME ------------------- */
#if (CPU_CFG_NAME_EN == DEF_ENABLED)CPU_NameInit();         //CPU 名字初始化
#endif
}

时间戳,它的精度高达ns级别,是CPU内核的一个重要资源。

在Cortex-M(注意:M0内核不可用)内核中有一个外设叫DWT(Data Watchpoint and Trace),是用于系统调试及跟踪, 它有一个32位的寄存器叫CYCCNT。
CYCCNT是一个向上的计数器,记录的是内核时钟运行的个数。
内核时钟跳动一次, 该计数器就加1,当CYCCNT溢出之后,会清零重新开始向上计数。
CYCCNT的精度非常高,其精度取决于内核的频率是多少, 如果是STM32F1系列,内核时钟是72M,那精度就是1/72M = 14ns,而程序的运行时间都是微秒级别的,所以14ns的精度是远远够的。
CYCCNT最长能记录的时间为:60s=2的32次方/72000000(假设内核频率为72M,内核跳一次的时间大概为1/72M=14ns), 而如果是STM32H7系列这种400M主频的芯片,那它的计时精度高达2.5ns(1/400000000 = 2.5)。
如果是i.MX RT1052这种比较厉害的处理器,最长能记录的时间为: 8.13s=2的32次方/528000000(假设内核频率为528M, 内核跳一次的时间大概为1/528M=1.9ns) 。

想要启用DWT外设,需要由另外的内核调试寄存器DEMCR的位24控制,写1启用,DEMCR的地址是0xE000 EDFC。
在这里插入图片描述
启用DWT_CYCCNT寄存器之前,先清零。
让我们看看DWT_CYCCNT的基地址,从ARM-Cortex-M手册中可以看到其基地址是0xE000 1004, 复位默认值是0,而且它的类型是可读可写的,我们往0xE000 1004这个地址写0就将DWT_CYCCNT清零了。

在这里插入图片描述
关于CYCCNTENA,它是DWT控制寄存器的第一位,写1启用,则启用CYCCNT计数器,否则CYCCNT计数器将不会工作,它的地址是0xE000EDFC。
在这里插入图片描述
所以想要使用DWT的CYCCNT步骤:

  • 先启用DWT外设,这个由另外内核调试寄存器DEMCR的位24控制,写1启用
  • 在启用CYCCNT寄存器之前,先清零。
  • 启用CYCCNT寄存器,这个由DWT的CYCCNTENA 控制,也就是DWT控制寄存器的位0控制,写1启用

这样子,我们就能去看看μC/OS的时间戳的初始化了

#define  DWT_CR      *(CPU_REG32 *)0xE0001000
#define  DWT_CYCCNT  *(CPU_REG32 *)0xE0001004
#define  DEM_CR      *(CPU_REG32 *)0xE000EDFC#define  DEM_CR_TRCENA                   (1 << 24)#define  DWT_CR_CYCCNTENA                (1 <<  0)#if (CPU_CFG_TS_TMR_EN == DEF_ENABLED)
void  CPU_TS_TmrInit (void)
{CPU_INT32U  cpu_clk_freq_hz;/* Enable Cortex-M3's DWT CYCCNT reg. */DEM_CR         |= (CPU_INT32U)DEM_CR_TRCENA;DWT_CYCCNT      = (CPU_INT32U)0u;DWT_CR         |= (CPU_INT32U)DWT_CR_CYCCNTENA;cpu_clk_freq_hz = BSP_CPU_ClkFreq();CPU_TS_TmrFreqSet(cpu_clk_freq_hz);
}
#endif

2. SysTick初始化

时钟节拍的频率表示操作系统每1秒钟产生多少个tick。
tick即是操作系统节拍的时钟周期,时钟节拍就是系统以固定的频率产生中断(时基中断), 并在中断中处理与时间相关的事件,推动所有任务向前运行。
时钟节拍需要依赖于硬件定时器,在STM32 裸机程序中经常使用的SysTick时钟是 MCU的内核定时器,通常都使用该定时器产生操作系统的时钟节拍。

用户需要先在“ os_cfg_app.h”中设定时钟节拍的频率,该频率越高, 操作系统检测事件就越频繁,可以增强任务的实时性,但太频繁也会增加操作系统内核的负担加重,所以用户需要权衡该频率的设置。

我们在这里采用默认的 1000Hz(之后若无特别声明,均采用 1000 Hz),也就是时钟节拍的周期为 1 ms。

函数OS_CPU_SysTickInit()用于初始化时钟节拍中断,初始化中断的优先级,SysTick中断的启用等等,这个函数要跟不同的CPU进行编写, 并且在系统任务的第一个任务开始的时候进行调用,如果在此之前进行调用,可能会造成系统奔溃,因为系统还没有初始化好就进入中断, 可能在进入和退出中断的时候会调用系统未初始化好的一些模块

cpu_clk_freq = BSP_CPU_ClkFreq();	/* Determine SysTick reference freq. */
cnts = cpu_clk_freq / (CPU_INT32U)OSCfg_TickRate_Hz;
OS_CPU_SysTickInit(cnts); 	/*Init μC/OS periodic time src (SysTick).*/

3. 内存初始化

我们都知道,内存在嵌入式中是很珍贵的存在,而一个系统是软件,则必须要有一块内存属于系统所管理的。
所以在系统创建任务之前, 就必须将系统必要的东西进行初始化。
μC/OS采用一块连续的大数组作为系统管理的内存, CPU_INT08U Mem_Heap[LIB_MEM_CFG_HEAP_SIZE], 在使用之前就需要先将管理的内存进行初始化

Mem_Init();

5、任务调度器启动函数OSStart()

在创建完任务的时候,我们需要开启调度器。
因为创建仅仅是把任务添加到系统中,还没真正调度,那怎么才能让系统支持运行呢?
μC/OS为我们提供一个系统启动的函数接口——OSStart(),我们使用OSStart()函数就能让系统开始运行

void  OSStart (OS_ERR  *p_err)
{
#ifdef OS_SAFETY_CRITICALif (p_err == (OS_ERR *)0) {OS_SAFETY_CRITICAL_EXCEPTION();return;}
#endifif (OSRunning == OS_STATE_OS_STOPPED) {OSPrioHighRdy   = OS_PrioGetHighest();/* Find the highest priority */OSPrioCur       = OSPrioHighRdy;OSTCBHighRdyPtr = OSRdyList[OSPrioHighRdy].HeadPtr;OSTCBCurPtr     = OSTCBHighRdyPtr;OSRunning       = OS_STATE_OS_RUNNING;OSStartHighRdy();/* Execute target specific code to start task  */*p_err           = OS_ERR_FATAL_RETURN;/* OSStart() is not supposed to return  */}else{*p_err           = OS_ERR_OS_RUNNING; /* OS is already running */}
}

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

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

相关文章

STM32之HAL开发——I2C读写EEPROM

I2C功能框图&#xff08;F1系列&#xff09; STM32 的 I2C 外设可用作通讯的主机及从机&#xff0c;支持 100Kbit/s 和 400Kbit/s 的速率&#xff0c;支持 7 位、 10 位设备地址&#xff0c;支持 DMA 数据传输&#xff0c;并具有数据校验功能&#xff0c;I2C 外设还支持 SMBus2…

3D产品可视化SaaS

“我们正在走向衰退吗&#xff1f;” “我们已经陷入衰退了吗&#xff1f;” “我们正在步入衰退。” 过去几个月占据头条的问题和陈述引发了关于市场对每个行业影响的讨论和激烈辩论。 特别是对于科技行业来说&#xff0c;过去几周一直很动荡&#xff0c;围绕费用、增长和裁…

Webpack生成企业站静态页面 - ajax请求

一些项目因需求不同&#xff0c;如需SEO或小项目&#xff0c;使用angular、react或vue就大材小用了。可以通过webpack、gulp这些构建工具&#xff0c;也能快速完成html页面开发&#xff0c;并且也能使用less/sass/styus等样式预编译功能&#xff0c;以及将js、html分模块、分组…

RabbitMQ高级笔记

视频链接&#xff1a;【黑马程序员RabbitMQ入门到实战教程】 文章目录 1.发送者的可靠性1.1.生产者重试机制1.2.生产者确认机制1.3.实现生产者确认1.3.1.开启生产者确认1.3.2.定义ReturnCallback1.3.3.定义ConfirmCallback 2.MQ的可靠性2.1.数据持久化2.1.1.交换机持久化2.1.2.…

小米汽车SU7发布,售价21.59万起,订单总额破1亿

文 | 大力财经 小米召开新车发布会&#xff0c;正式发布小米 SU7。该车定位中大型纯电轿车&#xff0c;有 SU7、SU7 Pro、SU7 Max 三个版本&#xff0c;车身尺寸 4997/1963/1455mm&#xff0c;轴距 3000mm。售价 21.59-29.99 万。 新车发布后&#xff0c;市场反应热烈&#x…

Topaz Gigapixel AI for Mac 图像放大软件

Topaz Gigapixel AI for Mac是一款专为Mac用户设计的智能图像放大软件。它采用了人工智能技术&#xff0c;特别是深度学习算法&#xff0c;以提高图像的分辨率和质量&#xff0c;使得图像在放大后仍能保持清晰的细节。这款软件的特点在于其能够将低分辨率的图片放大至高分辨率&…

openPLC_Editor C语言编程 在mp157 arm板上调用io等使用记录

1.编程界面比较简单&#xff0c;具备PLC开发编程的四种编程方式。梯形图语言LD &#xff0c;指令表语言IL&#xff0c;结构化文本语言ST&#xff0c;功能模块图语言FBD。 2.官方使用手册。学习资料实在是太少&#xff0c;目前都是自己比较费劲的研究。 3.2 Creating Your First…

QT6实现音频输出方法

一.QT6音频调用及与QT5的区别 1.音频输入 QAudioSource代替QAudioInput类 QAudioSource类提供了一个接口&#xff0c;用于从音频输入设备接收音频数据。 Header: #include <QAudioSource> qmake: QT multimedia 2.音频输出 QAudioSink代替QAudioOutput类 QAudioSi…

【数据挖掘】实验5:数据预处理(2)

验5&#xff1a;数据预处理&#xff08;2&#xff09; 一&#xff1a;实验目的与要求 1&#xff1a;熟悉和掌握数据预处理&#xff0c;学习数据清洗、数据集成、数据变换、数据规约、R语言中主要数据预处理函数。 二&#xff1a;实验知识点总结 1&#xff1a;数据集成是将多个…

Topaz Video AI for mac 视频增强软件

Topaz Video AI for Mac是一款专为Mac用户设计的视频增强软件&#xff0c;它利用先进的人工智能技术和机器学习算法&#xff0c;为用户提供卓越的视频编辑和增强体验。 软件下载&#xff1a;Topaz Video AI for mac v4.2.2激活版 这款软件能够快速提高视频的清晰度、色彩饱和度…

激发数据潜力:企业数据中台的策略性构建与优化_光点科技

在信息时代&#xff0c;数据是企业价值链中不可或缺的一环。构建一个策略性的企业数据中台不仅能够整合分散的数据资源&#xff0c;还能提高决策效率和业务敏捷性。本文聚焦于如何策略性地构建和优化数据中台&#xff0c;以便企业能够最大化地利用数据资源&#xff0c;推动企业…

独立游戏《星尘异变》UE5 C++程序开发日志3——UEC++特供的数据类型

本篇日志将介绍FString&#xff0c;FText、FName的用法和相互转换&#xff0c;以及容器TMap&#xff0c;TArray的增删查改 一、字符串相关数据类型&#xff1a;FString、FText、FName FString是最接近std::string的类型&#xff0c;字符串本身可以看做一个存储char型的动态数…

【Django学习笔记(二)】CSS语言介绍

CSS语言介绍 前言正文1、CSS 快速了解2、CSS 应用方式2.1 在标签上应用2.2 在head标签中写style标签2.3 写到文件中 3、问题探讨&#xff1a;用Flask框架开发不方便4、选择器4.1 ID选择器4.2 类选择器4.3 标签选择器4.4 属性选择器4.5 后代选择器4.6 注意事项 5、样式5.1 高度和…

【RISC-V】如何使用release的risc-v gnu toolchain

riscv64-elf-ubuntu-22.04-gcc-nightly-2024.03.01-nightly.tar.gz 首先去release页面中获取相应的压缩包 将压缩包解压到想解压的位置&#xff0c;这里我选择了 mv Downloads/riscv64-elf-ubuntu-22.04-gcc-nightly-2024.03.01-nightly.tar.gz riscv64-tool-chain/然后解压…

python 字典练习

# 字典练习1 import time def main():month_income{1月: 8000, 2月: 8200, 3月: 7900, 4月: 6900, 5月: 8900, 6月: 12000, 7月: 8900, 8月: 6000,9月: 8900, 10月: 9200, 11月: 6200, 12月: 7000}year_income0for k,v in month_income.items():print(月份→,k,工资→,v)time.s…

中国香港男歌手张国荣 明星网页成品 html人物明星网页设计制作 明星前端网页开发 网页期末设计制作作业成品

中国香港男歌手张国荣 7页面 人物明星主题 带设计说明 jquery图片轮播特效 滚动文字 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns"http://ww…

【c++】类和对象(五)赋值运算符重载

&#x1f525;个人主页&#xff1a;Quitecoder &#x1f525;专栏&#xff1a;c笔记仓 朋友们大家好&#xff0c;本篇文章带大家认识赋值运算符重载&#xff0c;const成员&#xff0c;取地址及const取地址操作符重载等内容 目录 1.赋值运算符重载1.1运算符重载1.1.1特性&#…

刷题之动态规划

前言 大家好&#xff0c;我是jiantaoyab&#xff0c;开始刷动态规划的题目了&#xff0c;要特别注意初始化的时候给什么值。 动态规划5个步骤 状态表示 &#xff1a;dp数组中每一个下标对应值的含义是什么->dp[i]表示什么状态转移方程&#xff1a; dp[i] 等于什么1 和 2 是…

软考101-上午题-【信息安全】-网络安全

一、网络安全 1-1、安全协议 SSL(Secure Socket Layer&#xff0c;安全套接层)是 Netscape 于 1994年开发的传输层安全协议&#xff0c;用于实现 Web 安全通信。1996 年发布的 SSL3.0 协议草案已经成为一个事实上的Web 安全标准。 端口号是43。 SSL HTTP HTTPS TLS(Transpo…

XXE漏洞知识及ctfshow例题

XXE漏洞相关知识 XXE全称为XML Enternal Entity Injection 中文叫xml外部实体注入 什么是xml 简单了解XML&#xff1a; &#xff08;xml和html的区别可以简易的理解成&#xff1a;xml是用来储存数据和传输数据的而html是用来将数据展现出来&#xff09; XML 指可扩展标记语…