使用消息队列解决RTOS多线程同时打印串口乱序问题

解决STM32+FreeRTOS的printf重定向打印凌乱的问题 由于引入了FreeRTOS后,出现了任务调度,就出现了打印之间互相穿插,杂乱无章的情况。 打印如下所示: LED Running! Task test tTask test wo! Task tethree! st one! Task test tTask test wo! Task tethree! st one! LED Running! Task test tTask test wo! Ta... 阅读详情

背景

我们假定现在创建了3个工作线程,这3个线程都需要通过串口输出日志或内容。很简单的认为,我们可以做到各个任务连续输出自己线程的内容,实际上不是的,这涉及到了多线程原理,有时间跟大家说说,或者大家可以去了解了解。
在这篇文章只涉及题目提出的问题解决方法。

准备工作

  1. STM32 Cube系列软件开发工具
  2. 一块可以使用的STM32单片机
  3. 一个ST LINK 下载器 和 一个 USB 转 TTL
  4. 一个可以正常工作开发程序的电脑

开发环境

请自行搭建最基础的开发环境,能做到如下几点就可以了:

  1. 可以编写STM32程序并下载到单片机
  2. 重定向 printf 到串口
  3. 可以在串口看到正确的 你想要的代码输出结果
  4. FreeRTOS 使用 CMSIS V2 接口

问题复现

我们先复现一下 标题 中描述的问题:

使用cubemx配置我们初始化代码

配置USART1作为我们接下来例子中的使用串口

// printf 重定向代码 STM32H743
int __io_putchar(int ch){
	while(! (USART1->ISR & ((0x1UL << (6U)))));
	USART1->TDR = ch;
	return ch;
}

配置FreeRTOS,我们先添加3个线程,cubemx的图我就不放了(如果有需要,请在评论栏留言,我会找时间放图的),这里只放生成的代码。

  1. 线程ID 和 参数定义
/* Definitions for NormalTask01 */
osThreadId_t NormalTask01Handle;
const osThreadAttr_t NormalTask01_attributes = {
  .name = "NormalTask01",
  .stack_size = 128 * 4,
  .priority = (osPriority_t) osPriorityNormal,
};
/* Definitions for NormalTask03 */
osThreadId_t NormalTask03Handle;
const osThreadAttr_t NormalTask03_attributes = {
  .name = "NormalTask03",
  .stack_size = 128 * 4,
  .priority = (osPriority_t) osPriorityNormal,
};
/* Definitions for NormalTask02 */
osThreadId_t NormalTask02Handle;
const osThreadAttr_t NormalTask02_attributes = {
  .name = "NormalTask02",
  .stack_size = 128 * 4,
  .priority = (osPriority_t) osPriorityNormal,
};
  1. 在main中激活线程
/* creation of NormalTask01 */
NormalTask01Handle = osThreadNew(StartNormalTask01, NULL, &NormalTask01_attributes);
/* creation of NormalTask03 */
NormalTask03Handle = osThreadNew(StartNormalTask03, NULL, &NormalTask03_attributes);
/* creation of NormalTask02 */
NormalTask02Handle = osThreadNew(StartNormalTask02, NULL, &NormalTask02_attributes);
  1. 线程方法实现
void StartNormalTask01(void *argument)
{
  printf("Normal01 Task Start ... \r\n");
  for(;;)
  {
    osDelay(100);
  }
}
void StartNormalTask02(void *argument)
{
  printf("Normal02 Task Start ... \r\n");
  for(;;)
  {
    osDelay(1);
  }
}
void StartNormalTask03(void *argument)
{
  printf("Normal03 Task Start ... \r\n");
  for(;;)
  {
    osDelay(1);
  }
}
  1. 查看串口输出结果
    串口输出结果是不是跟我们想象中的结果不一样,我们想的是它会按照顺序输出
Normal01 Task Start ... 
Normal02 Task Start ... 
Normal03 Task Start ... 

实际上却是上面图片的结果,造成这样的原因就是多线程同时访问一个共享资源而造成资源的抢夺现象。

解决方法

新增一个串口守护线程,人为禁止其他线程访问串口资源,其他线程需要串口发送的内容则通过消息队列传递到串口守护线程统一进行输出。而消息队列先进先出的特性保证了我们串口输出不会乱序。

代码实现

  1. 新增一个串口输出线程和一个消息队列,消息队列作为其他线程传输内容到串口打印线程的媒介
/* Definitions for PrintTask */
osThreadId_t PrintTaskHandle;
const osThreadAttr_t PrintTask_attributes = {
  .name = "PrintTask",
  .stack_size = 128 * 4,
  .priority = (osPriority_t) osPriorityNormal,
};
/* Definitions for PrintQueue */
osMessageQueueId_t PrintQueueHandle;
const osMessageQueueAttr_t PrintQueue_attributes = {
  .name = "PrintQueue"
};
static char getPrintBufFromQueue[32];
  1. 在main中激活线程 和 初始化消息队列
PrintTaskHandle = osThreadNew(StartPrintTask, NULL, &PrintTask_attributes);
PrintQueueHandle = osMessageQueueNew (16, sizeof(getPrintBufFromQueue), &PrintQueue_attributes);
  1. 打印线程方法实现
void StartPrintTask(void *argument)
{
  /* USER CODE BEGIN 5 */
  /* Infinite loop */
  osStatus_t queueReturn = osOK;
  for(;;)
  {
	// 等待消息队列中的消息,timeout指定为osWaitForever
	queueReturn = osMessageQueueGet(PrintQueueHandle, getPrintBufFromQueue, NULL, osWaitForever);
	while(queueReturn == osOK){
		// 打印从消息队列中获取的字符串缓冲
		printf("%s", getPrintBufFromQueue);
		// 这里timeout指定为0,因为程序不需要阻塞在这里
		queueReturn = osMessageQueueGet(PrintQueueHandle, getPrintBufFromQueue, NULL, 0);
	}
  }
  /* USER CODE END 5 */
}
  1. 打印方法实现,注: 此处tempBuffer大小和消息队列的Item大小一致
#define fun_print(format, ...){ \
		char tempBuffer[32]; \
		memset(tempBuffer, 0, 32); \
		sprintf(tempBuffer, format, ##__VA_ARGS__); \
		osMessageQueuePut(PrintQueueHandle, tempBuffer, 0, 0); \
	}
  1. 此时在查看我们的串口输出结果,我们可以看到不会出现第一张图中的乱序
    串口输出结果

使用互斥量解决

#define mt_log_mutex myMutex01Handle
#define mt_log(format, ...) \
  osMutexAcquire(mt_log_mutex, osWaitForever); \
  printf(format, ##__VA_ARGS__); \
  osMutexRelease(mt_log_mutex);

(上述代码仅供交流使用,请勿直接用在生产环境,如有错误,请在评论栏提出讨论)

消息队列方式实现串口数据不定长接收 ---- RT-thread&STM32 一、串口数据不定长接收的实现 通常在裸机中,我们使用一个定时器来辅助串口实现串口数据不定长接收,也就是当串口接收数据时,定时器一直处于定时值(比如100ms),接收不断的把数据放入缓冲区(通常可使用数组),当串口空闲时,定时器开始计时,当计时时间到,读取缓冲区的数据即可,这样就实现了数据的不定长接收。 而使用RTOS,可以使用消息队列来作为缓冲区,串口每次就收到数据就放入消息队列中,然后别的对象(... 阅读详情

相关推荐

ML307C OPENCPU开发环境搭建完整指南:从零到跑通

在物联网通信方案中,Cat.1模组凭借适中的速率、低成本和成熟的网络覆盖,成为共享设备、支付终端及定位追踪等领域的热门选择。传统方案通常采用外置MCU通过AT指令控制模组,但硬件成本与开发复杂度较高。OPENCPU模式直接将应用代码运行于模组内置处理器,省去外部MCU,降低物料清单并简化系统结构,同时保留RTOS实时任务调度能力。开发者可以像操作单片机一样管理GPIO、串口与网络协议栈,适用于业务逻辑不复杂的联网产品。然而,实际搭建ML307C的OPENCPU开发环境涉及工具链配置、SDK导入、镜像编译与烧

weixin_34335458的博客 206

FreeRTOS线程安全、中断安全的printf实现方式

利用串口对基于FreeRTOS的多任务程序调试过程中,时常出现数据交叉打印,甚至出现乱码现象。这是因为通常情况下,串口打印函数所在任务的优先级不高,或者在程序中有多个任务均包含打印函数。此时,当发送数据时,容易被中断或者其它优先级更高的任务打断或抢占,在发送的数据量较大情况下,该现象尤为明显。 文本将介绍一种FreeRTOS的线程安全、中断安全的printf实现方式,并详细说明它是如何解决上述问题的。...

m0_64770246的博客 4138

嵌入式裸机程序迁移RTOS实战:从时间片轮询到FreeRTOS稳定架构

在嵌入式开发中,裸机程序设计常采用基于超级循环的时间片轮询机制,但随着业务复杂度提升,阻塞延时、外设响应冲突等问题会逐步暴露。RTOS提供抢占式调度,让任务按优先级独立运行,从根本上解决单线程循环的时序耦合问题。FreeRTOS作为轻量级实时内核,具备任务管理、队列通信、信号量同步和互斥量保护等核心机制,适用于物联网网关、工业控制等典型场景。本文通过真实项目迁移案例,剖析任务划分、优先级设计、中断与服务函数改造、栈空间配置等全流程实践,分享裸机代码向RTOS架构平滑过渡的技术路径与工程避坑指南,帮助开发者应

weixin_34199335的博客 421

RTOS和非实时的多线程操作系统的区别

**RTOS**:通常使用优先级或截止时间为基础的调度策略,确保高优先级的任务能够得到及时处理。- **非实时操作系统**:调度策略可能更加灵活,旨在优化系统的整体性能和资源利用率,而不是响应时间。- **非实时操作系统**:通常资源更丰富,系统的设计可承受较高的资源消耗,同时提供更多的功能和服务。- **RTOS**:为了保证实时性能,RTOS的系统开销(如上下文切换时间)通常非常低。- **非实时操作系统**:可能有更高的系统开销,因为它们设计用来处理更广泛的应用和服务。

Anonymous_0_的博客 796

STM32+FreeRTOS+LwIP移植实战:从多任务协同到内存管理详解

在嵌入式系统开发中,实时操作系统(RTOS)与TCP/IP协议栈的集成是实现复杂网络功能的关键。FreeRTOS作为一款开源、可裁剪的RTOS,与轻量级IP协议栈LwIP的结合,为STM32等资源受限的MCU提供了强大的网络通信能力。其核心原理在于通过操作系统模拟层(sys_arch)将LwIP的线程、信号量等抽象映射到FreeRTOS的原语,实现协议栈在多任务环境下的安全并发。这种集成带来了显著的技术价值:它解耦了网络处理与业务逻辑,通过任务优先级调度保障了系统实时性,并利用RTOS的同步机制避免了资源竞

weixin_30882895的博客 351

ArduPilot 底层换血:ChibiOS 为何成为飞控新基石?

实时操作系统(RTOS)是无人机飞控等安全关键系统的核心底座,决定任务调度的确定性与资源利用效率。ChibiOS 作为一款专为 STM32 等 MCU 设计的轻量级操作系统,以静态配置、固定优先级抢占和丰富外设栈著称,解决了早期 NuttX 方案在内存占用、驱动维护和板级适配上的诸多痛点。ArduPilot 通过 AP_HAL 抽象层无缝切换底层,并用 hwdef.dat 统一描述板级硬件,使新增飞控板的适配工作从修改 C 驱动变为编写配置文件,大幅降低移植门槛。本文从实际开发视角出发,分享在自研 STM3

weixin_33912246的博客 386

DRA7xx异构通信实战:打通HLOS与RTOS的共享内存与IPC设计

在嵌入式系统开发中,异构多核架构(如ARM Cortex-A与Cortex-M组合)是实现高性能与高实时性平衡的关键技术。其核心原理在于通过共享内存与处理器间通信(IPC)硬件机制,让运行Linux等高级操作系统(HLOS)的应用处理器与运行实时操作系统(RTOS)的微控制器协同工作。这种设计的技术价值在于能同时满足复杂应用功能与硬实时控制需求,广泛应用于汽车电子(如智能座舱)、工业控制(如PLC、网关)等领域。本文以德州仪器DRA7xx平台为例,深入探讨了如何基于共享内存和IPC构建高效、可靠的双核通信系

weixin_30736301的博客 396

RTOS多线程操作的基本原理与实现

因此,每个线程都会有用于堆栈的RAM空间,如果线程使用的RAM超过堆栈的数量,则会导致内存溢出或细微的错误。中断可能具有不同的优先级,例如,如果触发了一些低优先级的中断,则当前正在执行的线程将暂停,并且ISR会获得控制权。假如在这过程中有其他线程抢占了,其他线程同样抢占了当前线程V0、 V1,如果不对V0、 V1进行保存,那么下次回来执行当前线程,结果就会出错。比如,如果这是一个具有多层嵌套调用的GUI线程,则可能需要数个千字节,但如果它是一个流水灯的小线程,则可能几十字节就足够了。

嵌入式技术开发 1611

CMSIS-FreeRTOS深度解析:嵌入式RTOS合规性接口层揭秘

RTOS(实时操作系统)是嵌入式系统的核心调度基础,而CMSIS-RTOS v2作为ARM官方定义的标准化抽象层,将底层内核(如FreeRTOS)封装为可移植、可认证、生态一致的接口规范。其本质并非简单API包装,而是通过严格的行为契约、优先级映射、中断管理重定义和内存模型约束,实现跨厂商MCU与工具链的ABI兼容性。该规范显著提升IEC 62304、ISO 26262等安全标准下的代码审计效率与认证通过率,并支撑Keil MDK、STM32CubeMX等主流开发环境的工程化落地。本文聚焦CMSIS-Fre

weixin_34218579的博客 223

2026年理想汽车嵌入式岗位高频面试题(含参考答案)

文章摘要 本文针对理想汽车嵌入式开发岗面试,整理了45道高频题目,覆盖硬件外设、RTOS、C/C++、数据结构等核心模块。重点解析了I2C/SPI通信协议对比、RTOS项目优化经验、电机驱动原理及PID参数整定等关键技术点,提供详细参考答案,帮助求职者系统掌握嵌入式开发核心知识,提升面试通过率。内容兼顾基础理论与工程实践,适配校招与社招场景需求。

Open 73

ThreadX下UART串口通信架构设计与调试实践

UART串口通信是嵌入式系统中不可或缺的组成部分,其异步收发特性在裸机环境下往往依赖中断与全局变量,而引入实时操作系统后,任务调度与资源共享为串口数据收发的可靠性提出了更高要求。基于ThreadX内核,利用信号量、消息队列与互斥量等同步原语,可以将串口中断处理与业务逻辑彻底解耦,构建多串口并发、收发不阻塞的高效通信架构。该方案在工业协议网关、人机交互设备及日志调试场景中均有广泛应用价值。围绕ThreadX下UART的工程结构、线程模型、底层配置和实测调优,系统梳理了从物理层参数校验到状态机协议解析的完整链路

weixin_29168153的博客 324

从零手写700行RTOS内核:STM32上的任务调度与5个关键陷阱

在嵌入式开发中,理解实时操作系统(RTOS)的底层原理是进阶的关键。很多开发者熟练使用FreeRTOS等现成内核,却对任务调度器如何工作、上下文切换如何发生、信号量如何实现等待与唤醒知之甚少。本文以STM32F103为硬件平台,从零实现一个仅700行代码的微型RTOS内核,覆盖任务管理、基于SysTick的时基、PendSV异常切换、信号量与消息队列等核心模块。文章不仅展示TCB设计、初始栈帧伪造、临界区保护等关键代码,更重点剖析实践中最容易踩中的5个大坑:首次调度HardFault、中断优先级配置错误、临

weixin_34318272的博客 263

智能驾驶感知模块的测试评估方法(上)

对于智能驾驶系统而言,感知模块是开发难度最大、投入资源最多的核心部分。在智能驾驶开发团队内部,感知模块需要独立进行评测才能及早发现潜在缺陷或判断性能是否有改进。随着近几年BEV、OCC、E2E等新技术方案被越来越多企业应用,感知的评测变得越来越艰难,效率也变得越来越低下。本文作者基于在智驾测试行业多年经验,试图总结感知评测的基本规律和效率提升方法,以供读者一些启发。

vincent_321的博客 2557

嵌入式面试高频失分点:C语言、RTOS、硬件调试与项目深挖实战

嵌入式开发已从基础外设操作升级为多维度系统能力验证。理解C语言的volatile语义、内存对齐与字节序,本质是保障CPU与硬件交互的确定性;掌握RTOS任务调度、中断下半部机制及Linux内核模块生命周期,核心在于应对产线级实时性与资源竞争问题;而I2C/UART/ADC等硬件调试能力,必须依托示波器、逻辑分析仪和JTAG实测,而非理论复述。本文聚焦2025年一线企业真实面试中反复出现的‘C语言深度’‘操作系统原理’‘硬件基础’‘项目经验’四大高频模块,结合STM32、i.MX RT、RK3399等主流平台

weixin_34393428的博客 213

2022(一等奖)B360基于Arcpy与SVM的滑坡易发性评价

此外,在ArcGIS平台下,“易发性图预测(多进程)”工具的运行时间为2h 48m 3s,“易发性图预测(单进程)”工具的运行时间为5h 19m 27s,运行时间缩短了近2h 31m 24s,运行效率提高了47.39%。在ArcGIS Pro平台下,“易发性图预测(多进程)”工具的运行时间为20m 12s,“易发性图预测(单进程)”工具的运行时间为1h 26m 47s,将运行时间缩短了近1h 6m 35s,运行效率提高了76.72%。滑坡的发生受各种影响因素的影响,目前,影响因素的选择没有一个统一的标准。

2301_77310370的博客 1465

卡方检验的隐藏逻辑:为什么正态分布的平方和如此重要?

本文深入探讨了卡方检验的核心逻辑,揭示了正态分布平方和在统计检验中的重要性。通过分析卡方分布与伽马分布的关系,以及实际应用案例,帮助读者理解卡方检验的原理、局限及高级应用场景,为数据分析提供有力工具。

3a9bq4r8t2y的博客 206

FreeRTOS+POSIX兼容层:嵌入式多线程代码复用与移植实战

在多线程嵌入式开发中,代码复用与跨平台移植一直是工程实践中的核心痛点。POSIX作为可移植操作系统接口标准,定义了线程管理、同步原语、时钟定时等统一API,使得基于Linux/Unix编写的中间件能够在不同RTOS间迁移。FreeRTOS+POSIX兼容层正是官方为FreeRTOS提供的适配方案,通过将pthread、信号量、互斥锁、条件变量等POSIX调用映射到FreeRTOS内核原语,让开发者无需重写业务逻辑即可快速复用成熟生态。这一机制不仅大幅降低从Linux转向单片机开发的入门门槛,也为资源受限的嵌

weixin_33910137的博客 358

lattice Diamond Programmer程序下载

Lattice Diamond Programmer(支持Lattice FPGA与CPLD程序下载)

qq_44385725的博客 5169

嵌入式RTOS核心机制深度解析:任务调度、中断管理与内存控制

实时操作系统(RTOS)是嵌入式系统实现确定性响应的关键基础,其本质在于任务调度、中断管理、同步通信与内存分配四大核心机制的协同运作。FreeRTOS作为主流轻量级RTOS,通过抢占式调度器、可配置堆管理(heap_4)、多级中断优先级分组及丰富的同步原语(信号量、互斥量、队列、事件组、任务通知),为STM32等MCU提供可预测的实时行为。理解任务状态机、空闲任务作用、ISR安全边界、优先级继承原理及栈/堆溢出防控,直接决定系统稳定性与低功耗能力。本文聚焦RTOS内核机制的底层逻辑与工程失效模式,覆盖Fre

weixin_28692153的博客 187

数字经济新质生产力数字化转型数据要素流通交易解决方案:数据要素化演进路径与管理、公共数据授权运营平台体系、数据底座、典型应用场景

数字经济的发展依赖于数据要素的有效流通与交易,数字化转型的核心在于数据的全生命周期管理和价值释放。数据要素化通过数据资源化、资产化和资本化的“三化”过程,实现其经济与社会价值。数据管理涵盖采集、存储、加工、分析等环节,支持数据的度量与交换。公共数据、企业数据和个人数据通过场内、场外及二级市场交易实现资本化。公共数据开发利用面临隐私泄露、监督管理难等痛点,目标是构建授权运营平台体系,确保数据流通的安全与效率。平台体系包括统一门户、授权管理、运营管理、合规监督等核心功能,支持明文、密文和可信交换流通方式。数据底

weixin_44094929的博客 1000

Arcmap计算土方量教程

1. 创建现状TIN 结果数据: 2. 创建设计TIN 结果数据: 注意:现状TIN和设计TIN构建时候的坐标系不同,设置坐标系见最后的注意事项第1点,就是import(导入)那步骤,现状TIN构建时选择高程点数据或者等高线数据,设计TIN构建时候选择洱海面数据 3. TIN转栅格 改成你要存的数据库,注意中间不要有汉字 结果数据(设计TIN转栅格的同理) 4. 裁剪现状TIN转成的栅格 结果数据: 5. 计算土方量 结果数据: ...

欢迎关注,共同进步! 5014

嵌入式统一消息机制:模块解耦与零拷贝通信实战解析

在嵌入式系统开发中,模块间通信方式直接影响代码可维护性与运行效率。传统全局变量与函数直调容易导致耦合度高、数据拷贝频繁,难以应对复杂工程迭代。统一消息机制通过定义标准消息结构、使用环形队列与消息分发器,将模块间的依赖从编译期解耦到运行期,并借助指针传递实现零拷贝通信,显著降低内存开销与延迟。该机制适用于STM32、ESP32等主流MCU平台,既能运行在裸机环境,也可无缝移植到FreeRTOS等RTOS中,广泛覆盖多传感器数据采集、按键交互、IoT设备状态上报及模块化控制逻辑等场景。本文结合C语言实践,解析消

weixin_34320724的博客 295

实例讲解MOS管电源开关电路的软启动

1、控制电源开关的输入信号 Control 为低电平或高阻时,三极管Q2的基极被拉低到地,为低电平,Q2不导通,进而MOS管Q1的Vgs = 0,MOS管Q1不导通,+5V_OUT 无输出。此时将 Control 设为低电平,三极管Q2关闭,电容C1与G极相连端通过电阻R2放电,电压逐渐上升到5V,起到软关闭的效果。不过从作者的描述来看,只是电路参数有区别,能通过的电流、能承受的耐压等不一样,但是软启动的原理是一样的。从公式可以看出,当电容容量越大,电压越高,时间越短,电流就会越大,从而形成浪涌电流。

U_YAO的博客 2万+

嵌入式固件进阶:启动流程、故障定位与OTA升级实战

嵌入式系统开发中,固件代码只是表象,真正的技术壁垒在于设备全生命周期的可靠性设计。从MCU复位向量到Linux SoC的U-Boot与内核接力,启动流程决定了系统如何从混沌走向有序,是排查异常的首要入口。基于现场证据的故障定位方法论,则能帮助开发者从HardFault、栈溢出等千奇百怪的崩溃中快速锁定根因。而OTA升级工程化,通过A/B双分区、状态机与签名校验等机制,将简单的文件烧写升级为可控、可回滚的完整闭环。这些底层能力不仅支撑着工业控制、智能硬件与车联网等场景的产品落地,更是嵌入式工程师从“能跑”迈向

weixin_33857230的博客 361

ADS 2024实战:手把手教你搞定2.3-3.5GHz宽带Doherty功放仿真与版图(附源文件)

本文详细解析了在ADS 2024环境中实现2.3-3.5GHz宽带Doherty功放的全流程设计,包括仿真与版图实现。从软件环境搭建、器件模型导入到核心电路设计、匹配网络优化,再到系统集成与版图生成,提供了实战技巧和关键参数设置。特别针对5G通信和卫星应用中的高效率需求,展示了如何通过ADS 2024的新功能提升设计效率。

weixin_42639246的博客 460

霍尼韦尔SoftMaster使用说明

霍尼韦尔SoftMaster使用说明

CMSIS-FreeRTOS深度解析:实时性、内存与中断的工程真相

CMSIS-FreeRTOS是ARM Cortex-M平台主流的标准化RTOS封装,其本质并非简单叠加,而是CMSIS-RTOS v2规范与FreeRTOS内核深度协同的共生体。它通过硬件抽象层(如SysTick/MPU/PendSV重定向)、资源胶水层(句柄池与类型封装)和工程集成层(RTE配置驱动)三层架构,实现跨芯片、跨工具链的可移植性,但代价是确定性实时性能的折损——例如osThreadFlagsSet()比xTaskNotifyFromISR()多耗142个CPU周期。技术价值在于支撑工业级7×2

weixin_33796205的博客 387

老版QQ原生表情gif动态

这是老版的QQ表情,不过全是动态的的,Windows照片查看器是不会动的,可以右击用浏览器打开查看,再说一遍是老版的,只是分享给宁愿用老版也要动态的人。

上一篇: python实现spring boot应用程序启动和停止的脚本
下一篇: MySQL NDB Cluster使用docker compose一键部署
zoutao98
博客等级 码龄9年 9粉丝 35原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值