【11】FreeRTOS的延时函数

news/2024/3/29 23:06:38/文章来源:https://blog.csdn.net/m0_52162042/article/details/129142300

目录

  • 1.延时函数-介绍
  • 2.相对延时函数-解析
    • 2.1函数`prvAddCurrentTaskToDelayedList`-解析
    • 2.3滴答定时器中断服务函数`xPortSysTickHandler()`-解析
    • 2.4函数`taskSWITCH_DELAYED_LISTS() `-解析
  • 3.延时函数-实验
  • 4.总结

1.延时函数-介绍

函数描述
vTaskDelay()相对延时
xTaskDelayUntil()绝对延时

相对延时:指每次延时都是从执行函数vTaskDelay()开始,直到延时指定的时间结束(任务被阻塞的时间,到调用此函数开始的时间)
绝对延时:指将整个任务的运行周期看成一个整体,适用于需要按照一定频率运行的任务(整个任务执行的时间,从头到尾的时间)

在这里插入图片描述
上图中的xTimeIncrement为绝对延时时间(假如绝对延时时间为100ms,那么以下三部分之和为100ms),包括以下三部分:
(1)为任务主体,也就是任务真正要做的工作;
(2)是任务函数中调用vTaskDelayUntil()对任务进行延时;
(3)为其他任务在运行(高优先级的任务进行抢占)

2.相对延时函数-解析

首先入口参数必须大于0,延时时间有效。

void vTaskDelay( const TickType_t xTicksToDelay ){BaseType_t xAlreadyYielded = pdFALSE;/* A delay time of zero just forces a reschedule. */if( xTicksToDelay > ( TickType_t ) 0U )

vTaskSuspendAll()挂起任务调度器,traceTASK_DELAY()函数并没有被实现。 prvAddCurrentTaskToDelayedList(点击函数名可跳转至解析)
将当前正在执行的任务移到阻塞列表。

            vTaskSuspendAll();{traceTASK_DELAY();/* A task that is removed from the event list while the* scheduler is suspended will not get placed in the ready* list or removed from the blocked list until the scheduler* is resumed.** This task cannot be in an event list as it is the currently* executing task. */prvAddCurrentTaskToDelayedList( xTicksToDelay, pdFALSE );}

恢复任务调度器。

            xAlreadyYielded = xTaskResumeAll();

判断xAlreadyYielded 是否需要进行任务切换。

        else{mtCOVERAGE_TEST_MARKER();}/* Force a reschedule if xTaskResumeAll has not already done so, we may* have put ourselves to sleep. */if( xAlreadyYielded == pdFALSE ){portYIELD_WITHIN_API();}else{mtCOVERAGE_TEST_MARKER();}}#endif /* INCLUDE_vTaskDelay */

此函数是将任务挂载到阻塞列表,解除是在滴答定时器的中断服务函数 xPortSysTickHandler()(点击函数名可跳转至解析)中。

2.1函数prvAddCurrentTaskToDelayedList-解析

函数prvAddCurrentTaskToDelayedList()有两个入口参数一个是延时时间xTicksToWait,另一个是xCanBlockIndefinitely 等于pdFALSE。

static void prvAddCurrentTaskToDelayedList( TickType_t xTicksToWait,const BaseType_t xCanBlockIndefinitely )

xConstTickCount 存储时钟节拍,滴答定时器中断一次,变量xTickCount加1。宏INCLUDE_xTaskAbortDelay 判断是否是中断延时,这里并没有使用,所以不用管。

{TickType_t xTimeToWake;const TickType_t xConstTickCount = xTickCount;#if ( INCLUDE_xTaskAbortDelay == 1 ){/* About to enter a delayed list, so ensure the ucDelayAborted flag is* reset to pdFALSE so it can be detected as having been set to pdTRUE* when the task leaves the Blocked state. */pxCurrentTCB->ucDelayAborted = pdFALSE;}#endif

将当前正在执行的任务的状态列表项使用函数uxListRemove()从就绪列表中移除,移除完判断是否有同等优先级的任务,没有就代表只有这一个任务,被移除掉后就绪列表中剩余任务为0,那么将此优先级的任务优先级复位。

    /* Remove the task from the ready list before adding it to the blocked list* as the same list item is used for both lists. */if( uxListRemove( &( pxCurrentTCB->xStateListItem ) ) == ( UBaseType_t ) 0 ){/* The current task must be in a ready list, so there is no need to* check, and the port reset macro can be called directly. */portRESET_READY_PRIORITY( pxCurrentTCB->uxPriority, uxTopReadyPriority ); /*lint !e931 pxCurrentTCB cannot change as it is the calling task.  pxCurrentTCB->uxPriority and uxTopReadyPriority cannot change as called with scheduler suspended or in a critical section. */}else{mtCOVERAGE_TEST_MARKER();}

INCLUDE_vTaskSuspend 判断是否使能挂起,判断延时时间xTicksToWait等于最大延时时间并且xCanBlockIndefinitely 不等于pdFALSE,此时将任务挂载到挂起列表中,由于传入参数为pdFALSE,所以不会挂载到挂起列表中,则执行else内容。

   #if ( INCLUDE_vTaskSuspend == 1 ){if( ( xTicksToWait == portMAX_DELAY ) && ( xCanBlockIndefinitely != pdFALSE ) ){/* Add the task to the suspended task list instead of a delayed task* list to ensure it is not woken by a timing event.  It will block* indefinitely. */listINSERT_END( &xSuspendedTaskList, &( pxCurrentTCB->xStateListItem ) );}       

else中首先记录时间,xConstTickCount 为进入函数prvAddCurrentTaskToDelayedList()时记录的时间,加上延时时间xTicksToWait就是任务到截止阻塞时间该被恢复的时间;通过函数listSET_LIST_ITEM_VALUE将延时时间写入到列表项值里,此值将用作挂载到阻塞列表时根据此值进行升序排列;

          else{/* Calculate the time at which the task should be woken if the event* does not occur.  This may overflow but this doesn't matter, the* kernel will manage it correctly. */xTimeToWake = xConstTickCount + xTicksToWait;/* The list item will be inserted in wake time order. */listSET_LIST_ITEM_VALUE( &( pxCurrentTCB->xStateListItem ), xTimeToWake );

判断需要等待截止的时间是否小于进入函数prvAddCurrentTaskToDelayedList()时记录的时间,这里判断是否数值溢出,如果溢出就将任务挂载到溢出阻塞列表中,否则挂载到阻塞列表中。

if( xTimeToWake < xConstTickCount ){/* Wake time has overflowed.  Place this item in the overflow* list. */vListInsert( pxOverflowDelayedTaskList, &( pxCurrentTCB->xStateListItem ) );}else{/* The wake time has not overflowed, so the current block list* is used. */vListInsert( pxDelayedTaskList, &( pxCurrentTCB->xStateListItem ) );

判断下一个阻塞超时时间如果大于新的阻塞时间,那么将新的阻塞时间更新为下一个阻塞超时时间。例如下一个xNextTaskUnblockTime 超时时间为30ms,新的阻塞时间xTimeToWake为20ms,肯定是20ms的先来到,所以将下一个阻塞超时时间更新为20ms。

                    /* If the task entering the blocked state was placed at the* head of the list of blocked tasks then xNextTaskUnblockTime* needs to be updated too. */if( xTimeToWake < xNextTaskUnblockTime ){xNextTaskUnblockTime = xTimeToWake;}else{mtCOVERAGE_TEST_MARKER();}}}}

2.3滴答定时器中断服务函数xPortSysTickHandler()-解析

函数xTaskIncrementTick()的值如果不等于pdFALSE,则进行任务切换,触发PendSV中断。

void xPortSysTickHandler( void )
{/* The SysTick runs at the lowest interrupt priority, so when this interrupt* executes all interrupts must be unmasked.  There is therefore no need to* save and then restore the interrupt mask value as its value is already* known - therefore the slightly faster vPortRaiseBASEPRI() function is used* in place of portSET_INTERRUPT_MASK_FROM_ISR(). */vPortRaiseBASEPRI();{/* Increment the RTOS tick. */if( xTaskIncrementTick() != pdFALSE ){/* A context switch is required.  Context switching is performed in* the PendSV interrupt.  Pend the PendSV interrupt. */portNVIC_INT_CTRL_REG = portNVIC_PENDSVSET_BIT;}}vPortClearBASEPRIFromISR();
}

在函数xTaskIncrementTick()中判断任务是否需要被解除。首先判断任务调度器是否被挂起,如果等于pdFALSE 则没有被挂起,进入if内容。

BaseType_t xTaskIncrementTick( void )
{TCB_t * pxTCB;TickType_t xItemValue;BaseType_t xSwitchRequired = pdFALSE;/* Called by the portable layer each time a tick interrupt occurs.* Increments the tick then checks to see if the new tick value will cause any* tasks to be unblocked. */traceTASK_INCREMENT_TICK( xTickCount );if( uxSchedulerSuspended == ( UBaseType_t ) pdFALSE )

将系统时钟节拍xTickCount加1,然后再将值赋给自己,没进来一次时钟节拍将自加1。

/* Minor optimisation.  The tick count cannot change in this* block. */const TickType_t xConstTickCount = xTickCount + ( TickType_t ) 1;/* Increment the RTOS tick, switching the delayed and overflowed* delayed lists if it wraps to 0. */xTickCount = xConstTickCount;

判断xConstTickCount 是否为0,为0则值溢出,进入函数 taskSWITCH_DELAYED_LISTS()(点击函数名可跳转至解析)

if( xConstTickCount == ( TickType_t ) 0U ) /*lint !e774 'if' does not always evaluate to false as it is looking for an overflow. */{taskSWITCH_DELAYED_LISTS();}else{mtCOVERAGE_TEST_MARKER();}/* See if this tick has made a timeout expire.  Tasks are stored in* the  queue in the order of their wake time - meaning once one task* has been found whose block time has not expired there is no need to* look any further down the list. */

判断当前时钟节拍ConstTickCount 是否大于等于下一个阻塞超时时间。

       /* See if this tick has made a timeout expire.  Tasks are stored in* the  queue in the order of their wake time - meaning once one task* has been found whose block time has not expired there is no need to* look any further down the list. */if( xConstTickCount >= xNextTaskUnblockTime ){for( ; ; ){

判断阻塞列表中是否有任务,如果没有任务则没有需要被解除的任务,则将下一个阻塞超时时间xNextTaskUnblockTime设置为最大值。

              if( listLIST_IS_EMPTY( pxDelayedTaskList ) != pdFALSE ){/* The delayed list is empty.  Set xNextTaskUnblockTime* to the maximum possible value so it is extremely* unlikely that the* if( xTickCount >= xNextTaskUnblockTime ) test will pass* next time through. */xNextTaskUnblockTime = portMAX_DELAY; /*lint !e961 MISRA exception as the casts are only redundant for some ports. */break;}

else则阻塞列表中有任务,通过函数listGET_OWNER_OF_HEAD_ENTRY()获取阻塞列表的第一个成员的任务控制块;通过函数listGET_LIST_ITEM_VALUE()获取列表项的数值,列表项中一般存放的是阻塞时间,则xItemValue被赋值阻塞时间。

               else{/* The delayed list is not empty, get the value of the* item at the head of the delayed list.  This is the time* at which the task at the head of the delayed list must* be removed from the Blocked state. */pxTCB = listGET_OWNER_OF_HEAD_ENTRY( pxDelayedTaskList ); /*lint !e9079 void * is used as this macro is used with timers and co-routines too.  Alignment is known to be fine as the type of the pointer stored and retrieved is the same. */xItemValue = listGET_LIST_ITEM_VALUE( &( pxTCB->xStateListItem ) );

判断系统时间节拍的数值是否小于阻塞时间,代表此时发生异常。因为在首先进入if时判断了当前的系统时钟节拍比下一个阻塞超时时间大。此时将列表项的值赋值给下一个阻塞超时时间,退出。

if( xConstTickCount < xItemValue ){/* It is not time to unblock this item yet, but the* item value is the time at which the task at the head* of the blocked list must be removed from the Blocked* state -  so record the item value in* xNextTaskUnblockTime. */xNextTaskUnblockTime = xItemValue;break; /*lint !e9011 Code structure here is deemed easier to understand with multiple breaks. */}else{mtCOVERAGE_TEST_MARKER();}

下面的情况为正常执行的情况。使用函数listREMOVE_ITEM()将任务从阻塞列表中移除,同时也从使用函数listREMOVE_ITEM从事件列表中移除。

                    /* It is time to remove the item from the Blocked state. */listREMOVE_ITEM( &( pxTCB->xStateListItem ) );/* Is the task waiting on an event also?  If so remove* it from the event list. */if( listLIST_ITEM_CONTAINER( &( pxTCB->xEventListItem ) ) != NULL ){listREMOVE_ITEM( &( pxTCB->xEventListItem ) );}else{mtCOVERAGE_TEST_MARKER();}

使用函数prvAddTaskToReadyList()将任务添加到就绪列表中,

                    /* Place the unblocked task into the appropriate ready* list. */prvAddTaskToReadyList( pxTCB );

判断宏configUSE_PREEMPTION是否使能抢占式任务调度,是则判断恢复的任务的任务优先级是否比当前正在执行的任务优先级高,是则将任务切换xSwitchRequired变量赋值pdTRUE。

                    /* A task being unblocked cannot cause an immediate* context switch if preemption is turned off. */#if ( configUSE_PREEMPTION == 1 ){/* Preemption is on, but a context switch should* only be performed if the unblocked task has a* priority that is equal to or higher than the* currently executing task. */if( pxTCB->uxPriority >= pxCurrentTCB->uxPriority ){xSwitchRequired = pdTRUE;}else{mtCOVERAGE_TEST_MARKER();}}#endif /* configUSE_PREEMPTION */}}}

以下程序是时间片调度:

        /* Tasks of equal priority to the currently running task will share* processing time (time slice) if preemption is on, and the application* writer has not explicitly turned time slicing off. */#if ( ( configUSE_PREEMPTION == 1 ) && ( configUSE_TIME_SLICING == 1 ) ){if( listCURRENT_LIST_LENGTH( &( pxReadyTasksLists[ pxCurrentTCB->uxPriority ] ) ) > ( UBaseType_t ) 1 ){xSwitchRequired = pdTRUE;}else{mtCOVERAGE_TEST_MARKER();}}#endif /* ( ( configUSE_PREEMPTION == 1 ) && ( configUSE_TIME_SLICING == 1 ) ) */#if ( configUSE_TICK_HOOK == 1 ){/* Guard against the tick hook being called when the pended tick* count is being unwound (when the scheduler is being unlocked). */if( xPendedTicks == ( TickType_t ) 0 ){vApplicationTickHook();}else{mtCOVERAGE_TEST_MARKER();}}#endif /* configUSE_TICK_HOOK */#if ( configUSE_PREEMPTION == 1 ){if( xYieldPending != pdFALSE ){xSwitchRequired = pdTRUE;}else{mtCOVERAGE_TEST_MARKER();}}#endif /* configUSE_PREEMPTION */}else{++xPendedTicks;/* The tick hook gets called at regular intervals, even if the* scheduler is locked. */#if ( configUSE_TICK_HOOK == 1 ){vApplicationTickHook();}#endif}return xSwitchRequired;
}

2.4函数taskSWITCH_DELAYED_LISTS() -解析

只溢出之后,将就绪列表pxDelayedTaskList和溢出就绪列表pxOverflowDelayedTaskList进行互换。

/* pxDelayedTaskList and pxOverflowDelayedTaskList are switched when the tick* count overflows. */
#define taskSWITCH_DELAYED_LISTS()                                                \{                                                                             \List_t * pxTemp;                                                          \\/* The delayed tasks list should be empty when the lists are switched. */ \configASSERT( ( listLIST_IS_EMPTY( pxDelayedTaskList ) ) );               \\pxTemp = pxDelayedTaskList;                                               \pxDelayedTaskList = pxOverflowDelayedTaskList;                            \pxOverflowDelayedTaskList = pxTemp;                                       \xNumOfOverflows++;                                                        \prvResetNextTaskUnblockTime();                                            \}

3.延时函数-实验

1、实验目的:学习 FreeRTOS 相对延时和绝对延时API 函数的使用,并了解其区别
2、实验设计:将设计三个任务:start_task、task1,task2 三个任务的功能如下:
start_task:用来创建task1和task2任务 task1用于展示相对延时函数vTaskDelay ( )的使用;
task1:用于展示相对延时函数vTaskDelay ( )的使用;
task2:用于展示绝对延时函数vTaskDelayUntil( )的使用 。
为了直观显示两个延时函数的区别,将使用LED0(PB1) 和LED1(PB0) 的翻转波形来表示

1.首先删除无关的程序内容,task1和task2程序如下,其他程序保持不变。

/* 任务1,用于展示相对延时函数vTaskDelay ( )的使用 */
void task1( void * pvParameters )
{while(1){LED0=~LED0;vTaskDelay(500);}
}
/* 任务2,用于展示绝对延时函数vTaskDelayUntil( )的使用 */
void task2( void * pvParameters )
{while(1){vTaskDelay(10);}
}

2.task1中的本身就是使用的相对于延时,相对于调用的时候起,到延时时间结束,所以不用改变;task2在使用绝对延时函数vTaskDelayUntil()时,可以查看FreeRTOS官网对该函数的使用介绍,根据示例来编写函数使用,绝对延时是整个task2运行的时间。
在这里插入图片描述
由于LED翻转语句执行较快基本看不到差距,所以这里加了死延时。

/* 任务1,用于展示相对延时函数vTaskDelay ( )的使用 */
void task1( void * pvParameters )
{while(1){LED0=~LED0;delay_ms(20);vTaskDelay(500);}
}
/* 任务2,用于展示绝对延时函数vTaskDelayUntil( )的使用 */
void task2( void * pvParameters )
{TickType_t xLastWakeTime;xLastWakeTime = xTaskGetTickCount(); /* 获取当前的系统时钟节拍 */while(1)	{LED1=~LED1;delay_ms(20);vTaskDelayUntil(&xLastWakeTime,500);}
}

这里由于手头没有示波器,我并没有做出实验结果,理论结果如下。
理论实验结果
由于task1和task2都加了死延时20ms,LED0翻转周期为520ms左右,而LED1的翻转周期仍为500ms。由于task2优先级比task1要高,task2会抢占task1,所以在执行时,会出现task1处于阻塞延时时结束时,task2处于死延时,此时并不能进行任务切换,task1会等到task2死延时结束进入阻塞延时再运行,LED0的亮灭时间会有所变化,LED0亮灭时间会变长,LED1的亮灭周期为500ms保持不变。将task1的优先级变高,将会影响task2的绝对延时时间。

4.总结

在这里插入图片描述

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

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

相关文章

CTFer成长之路之SSRF漏洞

SSRF漏洞CTF SSRF Training 题目描述: web容器中存在一个flag&#xff0c;mysql中存在一个管理员账号密码&#xff0c;其余容器中均没有特定flag mysql容器中内置 tcpdump vulnweb容器中内置一个 fpm.py 攻击脚本 docker-compose.yml version: "3" services:w…

Spring代理模式——静态代理和动态代理

✅作者简介&#xff1a;2022年博客新星 第八。热爱国学的Java后端开发者&#xff0c;修心和技术同步精进。 &#x1f34e;个人主页&#xff1a;Java Fans的博客 &#x1f34a;个人信条&#xff1a;不迁怒&#xff0c;不贰过。小知识&#xff0c;大智慧。 &#x1f49e;当前专栏…

笔记本电脑电池和充电器CE认证IEC62133测试

EC 符合性声明 - 您可以从品牌所有者或欧盟境内的商品官方进口商处获取此文件。证明您的商品经过检测符合下表所列标准的文件。您也可以从品牌所有者或欧盟境内的官方进口商处获取此文件。原品牌的笔记本电脑或手机&#xff08;如三星、苹果、戴尔、惠普等&#xff09;提供的原…

【验证码的识别】—— 点触式验证码的识别

一、前言 大家好&#xff0c;不知不觉的我来csdn已经又一周年了&#xff0c;在这一年里&#xff0c;我收获了很多东西&#xff0c;我是2022年2月22日入驻CSDN的&#xff0c;一开始只是为了方便浏览文章的&#xff0c;后来&#xff0c;我也有事没事发发文章&#xff0c;创作了1…

leetcode 1011. Capacity To Ship Packages Within D Days(D天内运送包裹的容量)

数组的每个元素代表每个货物的重量&#xff0c;注意这个货物是有先后顺序的&#xff0c;先来的要先运输&#xff0c;所以不能改变这些元素的顺序。 要days天内把这些货物全部运输出去&#xff0c;问所需船的最小载重量。 思路&#xff1a; 数组内数字顺序不能变&#xff0c;就…

Python 自动化测试必会技能板块—unittest框架

说到 Python 的单元测试框架&#xff0c;想必接触过 Python 的朋友脑袋里第一个想到的就是 unittest。的确&#xff0c;作为 Python 的标准库&#xff0c;它很优秀&#xff0c;并被广泛应用于各个项目。但其实在 Python 众多项目中&#xff0c;主流的单元测试框架远不止这一个。…

【C ++】C++入门知识(二)

C入门&#xff08;二&#xff09; 作者&#xff1a;小卢 专栏&#xff1a;《C》 喜欢的话&#xff1a;世间因为少年的挺身而出&#xff0c;而更加瑰丽。 ——《人民日报》 1.引用 1.1.引用的概念及应用 引用&#xff08;&&#xff09; 引用不是新定义一个变量&#xff0…

C语言格式化输出总结:%d,%c,%s,%f, %lf,%m.nd,%m.nf,%m.ns 以及sprintf函数

凡事发生必将有益于我&#xff0c;高手&#xff0c;从来都不仅仅是具备某种思维的人&#xff0c;而是那些具备良好学习习惯的人&#xff0c;成为高手&#xff0c;无他&#xff0c;手熟尔&#xff01;加油在最近的学习之中&#xff0c;对于格式化输出这个知识点&#xff0c;这里…

Spring自动装配的底层逻辑

Spring是如何自动装配Bean的&#xff1f;看源码一些自己的理解&#xff0c;如有错漏&#xff0c;请指正 使用Spring之前我们要先去web.xml中设置一下Spring的配置文件&#xff0c;在Spring的配置文件中&#xff0c;是通过component-scan扫描器去扫描base-package底下所有的类装…

google hacker语句

哎&#xff0c;我就是沾边&#xff0c;就是不打实战(&#xffe3;o&#xffe3;) . z Z 文章目录前言一、什么是谷歌Docker&#xff1f;二、受欢迎的谷歌docker语句谷歌docker的例子日志文件易受攻击的 Web 服务器打开 FTP 服务器SSH私钥电子邮件列表实时摄像机MP3、电影和 PDF…

Rocky 9.1操作系统实现zabbix6.0的安装部署实战

文章目录前言一. 实验环境二. 安装zabbix过程2.1. 安装zabbix源2.2 安装zabbix相关的软件2.3 安装数据库并启动2.4 开始初始化数据库&#xff1a;2.5 创建数据库实例及对应的用户2.6 导入官网提供的数据2.7 配置zabbix 服务的配置文件2.8. 启动服务2.9 从网页进行安装2.10 登陆…

从0开始学python -37

Python3 错误和异常 作为 Python 初学者&#xff0c;在刚学习 Python 编程时&#xff0c;经常会看到一些报错信息&#xff0c;在前面我们没有提及&#xff0c;这章节我们会专门介绍。 Python 有两种错误很容易辨认&#xff1a;语法错误和异常。 Python assert&#xff08;断…

单元测试面试秘籍分享

1. 什么是单元测试 “在计算机编程中&#xff0c;单元测试又称为模块测试&#xff0c;是针对程序模块来进行正确性检验的测试工作。程序单元是应用的最小可测试部件。在过程化编程中&#xff0c;一个单元就是单个程序、函数、过程等&#xff1b;对于面向对象编程&#xff0c;最…

代码随想录NO49 | 动态规划 _LeetCode1143.最长公共子序列 1035.不相交的线 53. 最大子序和

动态规划 _LeetCode1143.最长公共子序列 1035.不相交的线 53. 最大子序和今天继续子序列问题&#xff01; 1143.最长公共子序列 给定两个字符串 text1 和 text2&#xff0c;返回这两个字符串的最长 公共子序列 的长度。如果不存在 公共子序列 &#xff0c;返回 0 。 一个字符…

从功能测试(点点点)到进阶自动化测试,实现薪资翻倍我只用了3个月时间

前言 从事测试工作已3年有余了&#xff0c;今天想聊一下自己刚入门时和现在的今昔对比&#xff0c;虽然现在也没什么成就&#xff0c;只能说笑谈一下自己的测试生涯&#xff0c;各位看官就当是茶余饭后的吐槽吧&#xff0c;另外也想写一写自己的职场感想&#xff0c;希望对刚开…

如何使用 ESP-PROG 板的 Program 接口为 ESP32-S3-WROOM-1 系列的模组烧录固件?

ESP-PROG 是一款乐鑫推出的开发调试工具&#xff0c;具有自动下载固件、串口通信、JTAG 在线调试等功能。具体使用说明参见&#xff1a;ESP-Prog 下载与调试板介绍 。 ESP-Prog 采用 FTDI 公司的 FT2232HL 为 USB Bridge Controller 芯片&#xff0c;可通过配置将 USB 2.0 接口…

分布式链路追踪-skywalking

一、分布式调用链随着业务的高速发展&#xff0c;服务之间的调用关系愈加复杂线上每一个请求会经过多个业务系统&#xff0c;并产生对各种缓存或者DB 的访问&#xff0c;业务流会经过很多个微服务的处理和传递。问题&#xff1a;• —次请求的流量从哪个服务而来&#xff1f;最…

在CentOS-7.9配置vsftpd服务

文章目录一 vsftpd简介二 环境准备三 服务部署3.1 安装软件3.2 编写配置文件3.3 用户授权3.4 启动服务3.5 文件传输测试3.5.1 Windows到Linux3.5.2 filezilla3.5.3 从Linux到Linux一 vsftpd简介 FTP是 File Transfer Protocol 文件传输协议的简称。 VSFTP是 Very Security FTP…

ESP32-C3 BLE5.0 扩展蓝牙名称长度的流程

蓝牙设备名称长度受限于蓝牙广播数据包的长度&#xff0c;如果广播数据包的长度不能包含完整的设备名称&#xff0c;则只显示短名称&#xff0c;其余不能容纳的部分将被截断。ESP32-C3 支持 BLE5.0&#xff0c;最大广播包长支持 1650 字节&#xff0c;可通过 esp_ble_gap_confi…

PTA L1-054 福到了(详解)

前言&#xff1a;内容包括&#xff1a;题目&#xff0c;代码实现&#xff0c;大致思路&#xff0c;代码解读 题目&#xff1a; “福”字倒着贴&#xff0c;寓意“福到”。不论到底算不算民俗&#xff0c;本题且请你编写程序&#xff0c;把各种汉字倒过来输出。这里要处理的每…