第4章第3节 实时事件触发的实时抢…

第9章 中断 第9章 中断 cpsr 中断" TITLE="第9章 中断" /> 中断" TITLE="第9章 中断" /> 9.1.1 s3c2440中断控制器 中断方式:当某件事情发生时,硬件会设置某个寄存器;cpu在每执行完一个指令时,通过硬件查看这个寄存器,如果发生所关注的事件发生了,则中断当前程序流程,跳转到一个固定的地址处理这件事情。 中断" TITLE="第9章 中断" /> 阅读详情

源码请在https://github.com/ifreecoding/MbedRtos.git下载

本节用户代码在打印输出方面做了较大的修改,从本节开始,串口打印功能不再由任务实时向串口打印,而是由任务先将字符串打印到内存,然后使用一个低优先级的任务从内存中取出字符串打印到串口。串口是一个低速率的外设,9600的波特率差不多1ms打印1个字符,打印几十个字符的字符串时就需要几十ms,如果采用原有的打印方式,在打印字符串的过程中可能会发生任务切换,如果切换后的任务也在向串口打印字符串,那么这2个任务的字符就会混在一起,串口输出的字符发生混乱,得不到我们预期的输出结果。如果采用新的打印方式,每个任务需要从内存申请一个消息缓冲用来存放打印的字符,申请消息缓冲的过程非常快,可以将这一过程用中断锁住,防止其它任务干扰。消息缓冲对于任务来说,它是私有的,任务将字符串打印到消息缓冲的过程不会受任务切换的影响。当任务向消息缓冲打印完成后,先锁中断,然后将消息缓冲挂入消息队列,再解锁中断。消息打印任务会不断的查询消息队列,从队列中获取消息缓冲,将消息缓冲里面的数据打印到串口。向串口打印消息采用的是中断方式,消息打印任务将每条消息的第一个字符输出到串口,消息中剩下的其余字符会由上个字符触发的中断打印到串口,直至打印完最后一个字符。由于向串口打印数据是由同一个任务完成的,因此不会存在打印字符混乱的情况。

 


 

图 43  打印消息处理过程

我们可以定义一个消息缓冲池,所有的消息缓冲都在这里:

BUFPOOL gstrBufPool;

展开里面的结构体:

typedef struct bufpool

{

    M_CHAIN strFreeList;                

    MSGBUF astrBufPool[BUFPOOLNUM];     

}BUFPOOL;

typedef struct msgbuf

{

    M_CHAIN strChain;                   

    U8 ucLength;                        

    U8 ucCounter;                       

    U8 aucBuf[MSGBUFLEN];               

}MSGBUF;

其中strFreeList是空闲消息链表,初始化消息缓冲池的时候将所有消息缓冲挂接到这个链表上,申请消息缓冲时从该链表就可以获取到空闲的消息缓冲。当消息缓冲使用完毕后需要再挂接到这个链表上,可以继续重复使用。strChain是每个消息缓冲挂接到空闲消息链表的节点,ucLength中存储的是消息的长度,ucCounter中存储的是消息收发时的计数,aucBuf是存放消息的数组。

任务向消息缓冲填充完字符串之后,需要将消息挂入队列,目前的队列结构就是一个链表,后续章节我们会再补充一些东西进来。

typedef struct m_que    

{

    M_CHAIN strChain;   

}M_QUE;

关于消息打印部分的细节就不做过多介绍了,请读者自行参考代码。

在学习C语言时,我们都使用过printf函数,这个函数可以根据个人需要,很灵活的向显示器终端打印数据,比如,我们可以按照下面的方式输出:

printf("Wanlix & Mindows");

printf("%d, %c", i, j);

不知道你注意过没有,printf函数的参数个数是可变的,上面的第一个例子只有1个参数,第二个例子有3个参数,这点与我们一般所使用的函数是不同的。在Mindows中,我们仿造printf函数,将向内存打印的函数DEV_PutStrToMem也编写成一个参数可变的函数,下面我们一起来看看这是怎么实现的。

参数可变的函数原型定义为:

void DEV_PutStrToMem(U8* pvStringPt, ...);

其中比较特别的是“...”,这三个点表示省略的参数。可变参数函数的原理非常简单,可变参数函数的参数不像我们前面讲过的那样,使用R0~R3寄存器传递参数,而是直接使用堆栈传递参数,而且这些参数都是连在一起存放的,而函数原型中第一个参数是固定的,我们可以获取到第一个参数的地址&pvStringPt,然后将这个地址加4就可以得到第二个参数的地址,再加4就是第三个参数的地址,依次类推,这样就可以获取到任意多的参数地址了,有了参数地址那么获取参数的内容也就不成问题了。还有一个问题要解决,DEV_PutStrToMem函数是怎么知道它究竟有多少个参数的呢?注意到参数里的“%”号了么,每个%号对应一个参数,我们只需要查找%号的个数就可以确定可变参数的个数,再将每个可变参数转换为对应的%号后面的格式就可以实现DEV_PutStrToMem函数的功能了。

 


 

图 44  可变参数函数

可变参数函数的原型可以有多种形式,但必须要保证:

1.必须有第一个参数,通过这个参数才能获取到参数存放在栈中的首地址,后面的参数才是可变的。

2.在设计可变参数时需要能体现出可变参数的个数,如上面查找第一个参数中%号的方法,或者将第一个参数定义为可变参数的个数,或者其它方式。

在C语言标准头文件stdarg.h里面已经为可变函数定义了几个宏,使用这些宏也可以实现可变参数函数,原理都一样, 细节不再介绍了。

本节内容已经介绍完毕,设计一下测试函数来验证本节新增加的功能。本节共有4个测试函数TEST_TestTask1~TEST_TestTask4,每个函数的结构非常相似,循环执行打印、运行、延迟这3个过程。

00020  void TEST_TestTask1(void)

00021  {

00022      while(1)

00023      {

00024          DEV_PutStrToMem((U8*)"\r\nTask1 is running! Tick is: %d",

00025                          MDS_SystemTickGet());

00026  

00027          DEV_DelayMs(2000);

00028  

00029          (void)MDS_TaskDelay(150);

00030      }

00031  }

00038  void TEST_TestTask2(void)

00039  {

00040      while(1)

00041      {

00042          DEV_PutStrToMem((U8*)"\r\nTask2 is running! Tick is: %d",

00043                          MDS_SystemTickGet());

00044  

00045          DEV_DelayMs(1000);

00046  

00047          (void)MDS_TaskDelay(100);

00048      }

00049  }

00056  void TEST_TestTask3(void)

00057  {

00058      DEV_PutStrToMem((U8*)"\r\nTask3 is running! Tick is: %d", MDS_SystemTickGet());

00059  

00060      

00061      (void)MDS_TaskWake(gpstrTask4Tcb);

00062  

00063      while(1)

00064      {

00065          DEV_PutStrToMem((U8*)"\r\nTask3 is running! Tick is: %d",

00066                          MDS_SystemTickGet());

00067  

00068          DEV_DelayMs(5000);

00069  

00070          (void)MDS_TaskDelay(500);

00071      }

00072  }

00079  void TEST_TestTask4(void)

00080  {

00081      while(1)

00082      {

00083          DEV_PutStrToMem((U8*)"\r\nTask4 is running! Tick is: %d",

00084                          MDS_SystemTickGet());

00085  

00086          DEV_DelayMs(1000);

00087  

00088          (void)MDS_TaskDelay(1000);

00089      }

00090  }

不同的是TEST_TestTask1任务优先级为4,创建时不使用任务选项参数,默认为ready态。TEST_TestTask2任务优先级为6,创建时使用任务选项参数,为ready态。TEST_TestTask3任务优先级为2,创建时使用任务选项参数,为delay态,延迟2000个ticks,delay态结束后会使用MDS_TaskWake函数唤醒TEST_TestTask4任务。TEST_TestTask4任务优先级为1,创建时使用任务选项参数,为delay态,永久延迟。

00014  void MDS_RootTask(void)

00015  {

00016      M_TASKOPT strOption;

00017  

00018      

00019      DEV_SoftwareInit();

00020  

00021      

00022      DEV_HardwareInit();

00023  

00024      

00025      (void)MDS_TaskCreate((VFUNC)TEST_TestTask1, gaucTask1Stack, TASKSTACK, 4,

00026                           (M_TASKOPT*)NULL);

00027  

00028      

00029      strOption.ucTaskSta = TASKREADY;

00030      (void)MDS_TaskCreate((VFUNC)TEST_TestTask2, gaucTask2Stack, TASKSTACK, 6,

00031                           &strOption);

00032  

00033      

00034      strOption.ucTaskSta = TASKDELAY;

00035      strOption.uiDelayTick = 2000;

00036      (void)MDS_TaskCreate((VFUNC)TEST_TestTask3, gaucTask3Stack, TASKSTACK, 2,

00037                           &strOption);

00038  

00039      

00040      strOption.ucTaskSta = TASKDELAY;

00041      strOption.uiDelayTick = DELAYWAITFEV;

00042      gpstrTask4Tcb = MDS_TaskCreate((VFUNC)TEST_TestTask4, gaucTask4Stack, TASKSTACK,

00043                                     1, &strOption);

00044  

00045      (void)MDS_TaskDelay(DELAYWAITFEV);

00046  }

如果单从任务优先级来看,应该是TEST_TestTask4任务最先运行,但TEST_TestTask4任务处于永久delay状态,不能运行。剩下的优先级最高的任务是TEST_TestTask3,但TEST_TestTask3任务也处于delay状态,需要延迟2000个ticks后才能重新参与调度。TEST_TestTask1和TEST_TestTask2任务尽管使用了不同的任务参数,但都处于ready态,TEST_TestTask1的优先级高,先运行TEST_TestTask1,在TEST_TestTask1任务处于delay态时就会运行TEST_TestTask2任务。在前2000个ticks,TEST_TestTask1和TEST_TestTask2任务会交替运行,在2000个ticks时,TEST_TestTask3任务delay时间耗尽,参与调度,应该可以看到TEST_TestTask3任务会打断其它任务开始运行。在TEST_TestTask3运行之后它立刻唤醒了TEST_TestTask4任务,应该还可以看到在TEST_TestTask3任务运行后立刻被TEST_TestTask4任务打断了,此后这4个任务按照优先级的调度方式交替运行。

 


 

图 45  增加delay状态的4个任务运行结果

 从图45中我们可以看到输出结果与我们设想的是一样的,TEST_TestTask1任务最先开始运行,经过200个ticks进入delay状态,TEST_TestTask2任务开始运行,此后TEST_TestTask1与TEST_TestTask2任务交替运行。在2000个ticks的时候TEST_TestTask3的delay时间耗尽,抢占其它任务开始运行,然后又被TEST_TestTask4任务抢占,之后4个任务参与任务调度。

读者可以从网站下载视频观看串口输出的过程。在视频中我们看到要么是在一段时间内没有字符打印出来,要么是几个任务的字符一起打印出来。这是因为打印字符的任务处于最低优先级,只有当其它任务都处于delay状态时它才可以运行,将内存中的字符打印到串口上来。

我们在这4个函数里使用了DEV_DelayMs函数模拟任务的业务功能,该函数会连续运行几秒时间,这段时间内的CPU占有率是100%,因此字符打印到串口的时刻相比任务将字符打印到内存的时刻有较大的延迟,如果CPU占有率较低的话,我们可以看到几乎是实时的打印数据。

如何下载era5数据?(两种方式) 供水利大数据云计算课程同学参考使用# 阅读详情

相关推荐

DIY音乐播放器:用VS1053模块+Arduino打造你的专属MP3播放器(附完整代码)

本文详细介绍了如何使用VS1053音频解码模块与Arduino开发板,从零开始构建一个功能完备的桌面级MP3播放器。内容涵盖硬件架构设计、SPI总线共享、底层驱动编写、双缓冲区数据流管理以及用户交互实现,并提供完整的代码示例与性能优化建议,帮助创客打造高度可定制的专属音乐播放设备。

weixin_29189003的博客 287

【WB32库开发】第13章(下)DMA直接存储器访问——存储器到外设例程优化

上节使用的例程最后的实验结果不太理想,没有将我们想要的内容打印出来,那么本节课,我们就在上节使用的例程上进行优化。 为了保证学习效率,在上节课中笔者已经给出了本节内容要使用的代码,务必在学习完上一章内容后再进行本章内容的学习。 13.6 实验任务分析 本次实验的主要任务是通过DMAC实现存储器到外设的数据传输,但DMAC_MemoryToUart例程实验结果不太理想,因此需要重新设计程序,使之能够通过串口将内存中存取的数据打印出来。 且上节例程的格式过于臃肿,本节例程将为大家展示如何封装函数,并在主函数中调

北海村夫的博客 499

我从华为身上学到的项目管理经验 -- 需求篇

记录这三年,关于项目管理上的一些心得体会 – 需求篇对于需求,我们可以根据不同的角色、理解拆分成三个过程:简单来说就是:需求分析原始需求、 需求拆分为系统需求、 需求实现为功能需求**需求分析**: 将客户需求 输出成 需求描述。 需求经理需要把 需求 转换成 客户能够接受的 初始需求 IR(Initial Requirement) 对于用户来说,我只管提 我的原始需求是

minminzhe520的博客 3万+

printf()是如何与UART外设驱动函数“勾搭”起来的?

今天给大家分享的是。在嵌入式世界里,输出打印信息是一种非常常用的辅助调试手段,借助打印信息,我们可以比较容易地定位和分析程序问题。在嵌入式应用设计里实现打印信息输出的方式有很多,本系列将以 IAR 环境为例逐一介绍 ARM Cortex-M 内核 MCU 下打印信息输出方法。本篇是第一篇,我们先介绍最常见的输出打印信息方式,即利用 MCU 芯片内的硬件 UART 外设。

weixin_39266374的博客 834

INMP441麦克风与ESP32的I2S音频采集实战指南

本文提供了INMP441数字麦克风与ESP32开发板通过I2S接口进行高质量音频采集的完整实战指南。详细讲解了硬件连接、软件配置、数据读取及基础处理,并分享了常见的避坑技巧与性能优化建议,帮助开发者快速构建物联网音频应用。

weixin_28396629的博客 598

Python爬虫实战:巧用HEAD请求和Cookie令牌绕过沃尔玛反爬(附完整代码)

本文详细介绍了如何利用Python爬虫技术,通过HEAD请求和Cookie令牌绕过沃尔玛的反爬机制。文章深入解析了人机验证系统的运作原理,并提供了完整的工程化实现代码,包括令牌池设计、高并发架构和异常处理策略,帮助开发者高效采集电商数据。

weixin_29046035的博客 1007

第4章第2节 定时器触发的实时抢占…

源码请在https://github.com/ifreecoding/MbedRtos.git下载 第2节定时器触发的实时抢占调度 在第3章,我们依靠用户代码主动调用任务切换函数WLX_TaskSwitch实现了任务切换功能,这种任务调度方式的调度时机是固定死的,只有代码运行到WLX_TaskSw

ifreecoding的博客 622

FairyGUI深度解析:Unity复杂UI开发的高效解决方案与性能优化

在Unity游戏和应用开发中,UI系统的性能与开发效率是核心挑战。原生UGUI的组件化模型在应对复杂、动态界面时,常面临Draw Call过高、层级管理臃肿的问题。其原理在于,每个UI元素对应一个GameObject,渲染合批受限于Transform层级与材质状态,易导致性能瓶颈。FairyGUI(FGUI)作为一种专业的第三方UI解决方案,其技术价值在于采用了截然不同的“舞台-显示对象”渲染模型。该模型通过内部轻量级对象管理与自主合批系统,实现了极高的运行时效率与Draw Call优化。在应用场景上,FG

weixin_31315567的博客 341

第3章第4节 使用Wanlix编写交通红…

源码请在https://github.com/ifreecoding/MbedRtos.git下载 第4节使用Wanlix编写交通红绿灯控制系统 至此我们已经实现了一个非常简单、小巧的操作系统——Wanlix,简单到它只具备任务切换这一项任务管理功能,而且需要用户自己主动切换,实时性较差。但无

ifreecoding的博客 554

OTP/EEPROM 读取与处理

Camera OTP/EEPROM 读取与处理 文章目录Camera OTP/EEPROM 读取与处理1. OTP概念1.1 基本概念1.2 OTP存储类型1.3 OTP烧录信息类型1.4 OTP 作用2. 配置准备2.1 基本认识2.2 OTP/EEPROM配置3. OTP/EEPROM datasheet理解 以及 读取方式3.1 data sheet 内容3.2 一般OTP/EEPROM(e...

Understand-image 1万+

VSCode+PlatformIO新建工程卡成狗?3步提速大法实测有效(附镜像源配置)

针对VSCode+PlatformIO新建工程时因依赖下载缓慢导致的加载慢问题,本文提供了三步核心提速方案。通过配置Python环境变量、修改PlatformIO插件设置以使用国内镜像源,并优化pip配置文件,实测可大幅缩短工程创建时间,有效解决创建工程卡顿的痛点。

delta的博客 711

第4章第7节 二进制信号量(一)

目前更新到5.3节,请在http://dl.dbank.com/c02ackpwp6下载5.3节的全部文档 本节源代码请在http://dl.dbank.com/c0fp2g5z9s下载   第7节 二进制信号量 某些资源在同一时刻只可以被一个任务操作,实时操作系统的任务抢占特性会导致这些资源可能被多个任务同时操作,从而产生错误。本节将讲述二进制信号量的原理,可以利用二进制信号量保护这些资源

ifreecoding的博客 2039

使用 MATLAB/Simulink + Simscape Electrical + Simulink Design Optimization,构建一个基于SiC/GaN的Z源逆变器在储能系统中的软启

《手把手教你学Simulink:Z源逆变器在储能系统中的应用》摘要 本文详细介绍了使用MATLAB/Simulink构建Z源逆变器储能系统仿真模型的全过程。Z源逆变器凭借其独特的阻抗网络结构,解决了传统逆变器在直通保护和升压能力方面的痛点。文章通过6个关键步骤:主电路搭建、调制策略设计、软启动实现、仿真分析、效率对比和工程问题解决方案,完整展示了Z源逆变器从理论到仿真的实现路径。重点讲解了直通零矢量SPWM调制策略和渐进式升压比控制方法,验证了系统93.3%的效率和优异的抗扰性能。该模型为光伏储能、电动汽车

MHD0815的博客 122

[转]*PyQt4 精彩实例分析* 实例10 …

原文:http://blog.youkuaiyun.com/chumpklutz/article/details/6061077 本实例使用一个简单的例子说明如何在程序开发中使用Designer生成.ui文件。本实例利用Qt Designer生成了3个简单的ui,在使用时,两个ui插入到主程序的QTabWidget中,另一个ui由按钮触发弹出,如下图所示。主程序窗口TestDialog采用的是手动编写代码的

PandaraWen 宝盒 567

Statistics and Machine Learning Toolbox

Statistics and Machine Learning Toolbox(带书签),MATLAB统计学习工具

Data-Structures-and-Algorithm-Analysis-in-Java.pdf

作者: Weiss, Mark A. 出版社: Addison Wesley Data Structures and Algorithm Analysis in Java is an “advanced algorithms” book that fits between traditional CS2 and Algorithms Analysis courses. In the old ACM Curriculum Guidelines, this course was known as CS7. This text is for readers who want to learn good programming and algorithm analysis skills simultaneously so that they can develop such programs with the maximum amo...

第2章第1节 ARM7芯片基本结构

源码请在https://github.com/ifreecoding/MbedRtos.git下载 第1节ARM7芯片基本结构 ARM7芯片构架比较简单,32bits线性地址空间统一排列,任何地址都是唯一的,不同的片上资源及外设被分配到不同的地址空间,不同数据结构的指针固定为4字节长度,这相对51芯片来说方便很多也清晰很多,从用户编程的角度来看入手比较简单,因此本手册首先选用

ifreecoding的博客 900

复分析 阿尔福斯(Ahlfors) 第三版答案 英文

答案分为十章,有部分习题没有答案,全英文复分析阿尔福斯(Ahlfors) 第三版答案

第4章第7节 二进制信号量(三)

目前更新到5.3节,请在http://dl.dbank.com/c02ackpwp6下载5.3节的全部文档 本节源代码请在http://dl.dbank.com/c0fp2g5z9s下载   图53列出了多个不同状态的任务与这些调度表之间的关系,任务1被信号量sem1阻塞,处于非永久pend态,strDelayQue结构中的strQueHead节点被挂接到delay表,strTcbQue结构中的

ifreecoding的博客 647

锂电池充电器电路设计 里面有电路

锂电池充电器电路设计 里面有电路 3.7V升压到5V电路设计。

flex4 读书笔记 Chapter 4

原文地址:读书笔记 Chapter 4">flex4 读书笔记 Chapter 4作者:搏拼 1、flex包含一个基于组件的开发模型,你只需要按需求继承现有的组件并扩展相关的方法就可开发你的应用。 2、可视化组件包括以下主要特性:尺寸、事件、样式、效果、皮肤。 3、flex定义了两套组件。一套为Spark,放在spark.*包下。一套为MX,放在mx.*包下。Spark是在flex4中新添加的,

lanyanai的专栏 887

对&nbsp与select标签的认识

对&nbsp与select标签的认识初学JavaScript,在做表单的时候遇到一点问题,我也就写着玩玩哈,这都是些我个人的认解。 &nbsp(空格)每个网页的&nbsp(空格)显示宽度各不相同,比如:Mozilla Firefox 显示空格宽度是汉字的1/2,而谷歌 显示空格宽度是1个汉字。 所以我们可以用 对span 设置margin-left=2em 注意:span 中的字体大小要与外部字

shichao1994的博客 314

jquery选择器 参数为变量 如何解决

$(function(){    alert(123);    var v=4;    var test=$("input[type='radio'][value='"+v+"']");//直接拼接字符串就可以了   console.info(test);    var testValue=test.attr({"checked":true});    console.info(testVal

nihousheng的专栏 848

oracle细粒度审计 第 3 部分

oracle细粒度审计 第 3 部分 安全性和身份管理 现实问题的细粒度审计,第 3 部分 作者:Arup Nanda 现在您已经掌握了各种环境中的 FGA,下面您将了解到它如何能够在 Oracle Database 10g 中起到更大的作用 在本系列的前两个部分中,我向您介绍了细粒度审计 (FGA) 的概念,它用来在 Oracle9i Database 和更高版本中跟踪选定的语句。我还说

塔山上的山 364

phpMyAdmin 3.5.0 正式版发布

phpMyAdmin 3.5.0 正式版发布,该版本改进了浏览器的支持,包括表格编辑、记住历史表、记住最后排序的表和列宽等等,对很多操作进行 Ajax 处理,重新组织的服务器状态页,改进对存储过程、事件和触发器的支持,改进对 OpenGIS 支持 等等。 主要包括: 浏览器模式改进,包括表格式编辑,自动记忆最近使用过的数据表,自动记忆最近排序,可调整的列宽,更加紧凑的导航栏 操作 Ajax 化

tomora的专栏 550

wince6.0 +S3C6410 中断…

首先:介绍一下wince中断处理过程, 1、当一个中断发生后,发送到内核异常处理器(ExceptionHandler) 2、内核的中断支持处理器调用OAL层函数OEMInterruptDisable 屏蔽中断(..\hal\Cfw.c)      屏蔽掉所有优先级等于或是小于当前优先级的中断 3、内核调用ISR,ISR  返回中断号(逻辑中断标识符)给内核。       内核的处

caoshunxin01的专栏 192

AS3中 Event 类的target和cu…

原文地址:Event 类的target和currentTarget属性">AS3中 Event 类的target和currentTarget属性作者:笨鱼小宇  在事件处理过程中,会自动生成事件类的实例,并传给侦听器函数。通过这个参数就可以使用事件类的属性和方法。其中target与currentTarget属性是两个很相似的属性。  对于简单的事件处理过程,分清target与currentTa

211

Wince6.0 s3c6410触摸屏驱动分析

Wince6.0 s3c6410触摸屏驱动分析     一、wince 触摸屏驱动概述... 3 二、触摸屏驱动接口分析... 4 2.1  Windows CE触摸屏驱动工作过程... 4 2.2 触摸屏驱动程序的实现... 4 2.3四线电阻式触摸屏的工作原理... 4 三、wince 6.0 触摸屏驱动分析... 6 3.1  TouchPanelEnable() 函数分析... 6 3.

无语僧 1854
上一篇: 第4章第3节 实时事件触发的实时抢…
下一篇: 第4章第4节 任务切换钩子函数
ifreecoding
博客等级 码龄15年 148粉丝 33原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值