【STC8A8K64S4A12开发板】—片内存储器讲解

版权声明:本文为博主原创文章,转载请附上原文出处链接。


前言

今天介绍下STC8A8K64S4A12系列单片机片内存储器类型及各类型存储器之间的区别,掌握单片机读写内部EEPROM时的寄存器配置及程序设计。


一、硬件设计?

1.有关存储器概念介绍

RAM(全称是Random Access Memory)随机存储内存常称之为随机存储器,这种存储器在断电时将丢失其存储内容,故主要用于存储短时间使用的变量或程序。他的优势是可以随时读写,而且速度很快,通常作为操作系统或其他正在运行中的程序的临时数据存储媒介。

ROM(全称是Read-Only Memory)只读内存常称之为只读存储器,是一种只能读出事先所存数据的固态半导体存储器,他不像随机存储器那样可以快速、方便地改写存储于其中的数据。他的优势是所存数据稳定,断电后存储的数据也不会丢失。

EPROM(全称是Erasable Programmable Read-Only Memory)可擦除可编程只读存储器是一种可重写的存储器,并且其内容在掉电的时候也不会丢失。换句话说,他是非易失性的。他通过EPROM编程器进行编程,EPROM编程器能够提供比正常工作电压更高的电压对EPROM编程。一旦经过编程,EPROM只有在强紫外线的照射下才能够进行擦除。为了进行擦除,EPROM的陶瓷封装上具有一个小的石英窗口,这个石英窗口一般情况下使用不透明的粘带覆盖,当擦除时将这个粘带揭掉,然后放置在强紫外线下大约20分钟。

EEPROM(全称是Electrically Erasable Programmable Read-Only Memory)电可擦除可编程只读存储器的内容在掉电的时候也不会丢失。在平常情况下,EEPROM与EPROM一样是只读的,需要写入时,在指定的引脚加上一个高电压即可写入或擦除,而且其擦除的速度极快。通常EEPROM芯片又分为串行EEPROM和并行EEPROM两种,串行EEPROM在读写时数据的输入/输出是通过2线、3线、4线或SPI总线等接口方式进行的,而并行EEPROM的数据输入/输出则是通过并行总线进行的。

FLASH闪存结合了ROM和RAM的长处,不仅具备电可擦除可编程(EEPROM)的性能,还不会断电丢失数据、同时可以快速读取数据,所以现如今大多数单片机片内都集成了这种存储器。又因为FLASH相对于EEPROM(又称为E2PROM)成本要低得多,所以单片机片内往往会集成较大空间的FLASH存储器供用户使用。单片机的应用中常常将开发调试成功后的应用程序存储在程序存储器中,而FLASH存储器恰恰是非常理想的程序存储器。

FRAM(全称是Ferroelectric Random Access Memory)铁电体随机存储内存常称之为铁电存储器,这种存储器的核心技术是铁电晶体材料,这一特殊材料使得铁电存储器同时拥有随机存储器(RAM)和非易失性存储器的特性。

根据存储器是否在单片机内部,存储器可分为片内存储器和片外存储器。下面各存储器都是片外存储器。

在这里插入图片描述

图1:外部存储器实物图

☆注:根据存储器与单片机之间的接口是并行还是串行,又可将存储器分为串行存储器和并行存储器。

2.STC8A8K64S4A12系列单片机片内存储器介绍

STC8A8K64S4A12系列单片机片内有(8192+256)字节(一般为方便描述会说成8K字节)的数据存储器、16KB64KB存储空间不等的FLASH程序存储器和4KB48KB存储空间不等的E2PROM存储器。如下表所示。

表1:STC8A8K64S4A12系列单片机片内存储器
序号单片机型号SRAMFLASHE2PROM备注
1STC8A8K08S4A128K字节8K字节56K字节有仿真功能
2STC8A8K16S4A128K字节16K字节48K字节有仿真功能
3STC8A8K24S4A128K字节24K字节40K字节有仿真功能
4STC8A8K32S4A128K字节32K字节32K字节有仿真功能
5STC8A8K40S4A128K字节40K字节24K字节有仿真功能
6STC8A8K48S4A128K字节48K字节16K字节有仿真功能
7STC8A8K56S4A128K字节56K字节8K字节有仿真功能
8STC8A8K60S4A128K字节60K字节4K字节有仿真功能
9STC8A8K64S4A128K字节64K字节- -有仿真功能

■ SRAM和DRAM区别

SRAM(全称是Static Random Access Memory)静态随机存储内存常称之为静态存储器,所谓的“静态”,是指这种存储器只要保持通电,里面储存的数据就可以恒常保持。

DRAM(全称是Dynamic Random Access Memory)动态随机存储内存常称之为动态存储器,所谓的“动态”,是指这种存储器每隔一段时间,要刷新充电一次,否则内部的数据即会消失。

SRAM和DRAM都属于RAM存储器,在断电后存储的数据会全部丢失。SRAM优势是只需刷新充电一次,而DRAM则每隔一段时间就要刷新充电一次。SRAM劣势是相对DRAM集成度较低,功耗大,体积大,价格高。

☆注:STC8A8K64S4A12单片机片内集成的是SRAM。

■ STC8A8K64S4A12系列单片机片内SRAM

与程序存储器对应的另外一种存储器叫数据存储器,数据存储器可暂存运行期间的数据、中间结果、缓冲和标志位等,数据存储器一般选择RAM来存储数据。

STC8A8K64S4A12系列单片机片内RAM的类型是静态存储器SRAM,片内SRAM大小是(8192+256)字节,该(8192+256)字节SRAM在物理和逻辑上都分为2个地址空间:内部RAM(256字节)和内部扩展RAM(8192字节)。

STC8A8K64S4A12系列单片机256字节的内部RAM分3个部分:低128字节RAM(与传统8051兼容)、高128字节RAM(Intel在8052中扩展了高128字节RAM)及特殊功能寄存器区。高128字节RAM与特殊功能寄存器区貌似共用相同的地址范围,但物理上是独立的,使用时通过不同的寻址方式加以区分。如下图所示。

在这里插入图片描述

图2:STC8A8K64S4A12系列单片机内部RAM分配示意图

☆注:直接寻址、间接寻址、立即寻址,是CPU在通过总线与内存交互时存在不同的交互方法,而产生的三种概念词。这里不详细介绍,学过汇编的同学会非常容易理解他们之间的区别。

STC8A8K64S4A12系列单片机片内除了集成256字节SRAM外,内部还集成了8192字节的SRAM(常称之为内部扩展RAM),地址范围是:0000H~1FFFH。访问内部扩展RAM的方法和传统8051单片机访问外部扩展RAM的方法相同,但是STC8A8K64S4A12系列单片机访问内部扩展RAM不会影响P0口、P2口、WR引脚、RD引脚和ALE引脚。

STC8A8K64S4A12系列单片机片内扩展RAM是否可以访问受辅助寄存器的B1位控制,如下图所示。

在这里插入图片描述

图3:辅助寄存器AUXR

☆注:一般EXTRAM位默认是0,即允许使用逻辑上在片外、物理上在片内的扩展SRAM。

■ STC8A8K64S4A12系列单片机片内E2PROM
在项目应用中,往往会有一些很重要的数据是需要掉电不丢失的,这些数据需要被保存到非易失性的存储器里面,这些非易失性的存储器可以是单片机片内的非易失性存储器,也可以是单片机片外的非易失性存储器。STC8A8K64S4A12系列单片机片内集成了容量不等的E2PROM(STC8A8K64S4A12没有片内E2PROM),其与程序空间是分开的,擦写次数可达10万以上,可用于用户存放一些掉电不丢失的重要数据。

STC8A8K64S4A12系列单片机片内E2PROM是以扇区为单位进行操作的,每个扇区包含512字节。使用片内E2PROM时,同一次修改的数据建议放在同一个扇区,需要不同时修改的数据不要放在同一扇区,每个扇区不是一定要用满的。

表2:STC8A8K64S4A12系列单片机片内E2PROM空间大小及地址
序号单片机型号E2PROM扇区数E2PROM起始扇区首地址E2PROM结束扇区末尾地址备注
1STC8A8K08S4A1256K字节1120000hDFFFh这里的E2PROM起始扇区首地址和结束扇区末尾地址都是针对用IAP字节读时。(使用MOVC指令读取时地址不同)
2STC8A8K16S4A1248K字节960000hBFFFh这里的E2PROM起始扇区首地址和结束扇区末尾地址都是针对用IAP字节读时。(使用MOVC指令读取时地址不同)
3STC8A8K24S4A1240K字节800000h9FFFh这里的E2PROM起始扇区首地址和结束扇区末尾地址都是针对用IAP字节读时。(使用MOVC指令读取时地址不同)
4STC8A8K32S4A1232K字节640000h7FFFh这里的E2PROM起始扇区首地址和结束扇区末尾地址都是针对用IAP字节读时。(使用MOVC指令读取时地址不同)
5STC8A8K40S4A1224K字节480000h5FFFh这里的E2PROM起始扇区首地址和结束扇区末尾地址都是针对用IAP字节读时。(使用MOVC指令读取时地址不同)
6STC8A8K48S4A1216K字节320000h3FFFh这里的E2PROM起始扇区首地址和结束扇区末尾地址都是针对用IAP字节读时。(使用MOVC指令读取时地址不同)
7STC8A8K56S4A128K字节160000h1FFFh这里的E2PROM起始扇区首地址和结束扇区末尾地址都是针对用IAP字节读时。(使用MOVC指令读取时地址不同)
8STC8A8K60S4A124K字节80000h0FFFh这里的E2PROM起始扇区首地址和结束扇区末尾地址都是针对用IAP字节读时。(使用MOVC指令读取时地址不同)

☆注:STC8A8K64S4A12型号单片机的E2PROM大小可以在下载软件中配置,用户须知分配多少E2PROM存储空间,那么对应的FLASH存储空间就会减少多少。

STC8A8K64S4A12型号单片机配置的E2PROM是从FLASH空间的高存储区到低存储区规划的,配置方法如下图所示。

在这里插入图片描述

图4:STC-ISP下载界面E2PROM配置(针对STC8A8K64S4A12)

STC8A8K64S4A12系列单片机片内FLASH空白区域的填充值是可以配置的,该填充值可以是FF,也可以是00。在给单片机下载程序时,可按下图所示。

在这里插入图片描述

图5:STC-ISP下载界面选项注意事项

☆注:建议填充值选择FF。(默认选择也是FF)

二、软件设计

1.片内存储器寄存器汇集

STC8A8K64S4A12系列单片机进行片内存储器操作时会用到8个寄存器,如下表所示:

表3:STC8A8K64S4A12系列片内存储器操作使用寄存器汇总
序号寄存器名读/写功能描述
1AUXR读/写辅助寄存器
2IAP_DATA读/写IAP数据寄存器
3IAP_CMD读/写IAP命令寄存器
4IAP_TRIG读/写IAP触发寄存器
5IAP_CONTR读/写IAP控制寄存器
6IAP_ADDRH读/写IAP地址寄存器
7IAP_ADDRL读/写IAP地址寄存器

☆注:在操作STC8A8K64S4A12系列单片机片内E2PROM时,一定要谨慎定义操作地址(即ISP/IAP地址寄存器取值),以免对不该操作或者无效的区域进行操作。

2.寄存器解析

2.1.IAP命令寄存器IAP_CMD

IAP命令寄存器IAP_CMD的CMD0位和CMD1位组合用来选择对用户的应用程序区操作的模式。

在这里插入图片描述

图6: IAP命令寄存器IAP_CMD

2.2.IAP触发寄存器IAP_TRIG

IAP触发寄存器IAP_TRIG: 用于触发对用户的应用程序区的读、写、擦除操作。每次进行读、写或擦除操作时,需先使能IAPEN位(即将IAPEN位置1),再对IAP_TRIG寄存器先写0x5A,再写0xA5,这样操作命令才会生效。

在这里插入图片描述

图7: IAP命令触发寄存器

2.3.IAP控制寄存器IAP_CONTR

IAP控制寄存器IAP_CONTR主要是配置对用户的应用程序区的读、写、擦除操作时的功能选择,IAPEN是操作总开关,SWBS位和SWRST位配合使用,用于控制软件复位及开始执行程序区,CMD_FAIL位用于指示操作是否成功,不成功则会自动置1(需软件清零)。

在这里插入图片描述

图8:ISP/IAP控制寄存器

☆注:STC8A8K64S4A12系列单片机针对不同系统时钟推荐了等待时间设置值,用户须知该等待时间是硬件自动完成的,不需要用户在软件里面加额外的软件延时。另外,如果用户使用的是MOVC指令进行读取操作,则CPU不需要等待时间的。

3.片内EEPROM读写 - 单个字节(STC8A8K32S4A12)

☆注:本节的实验源码是在“实验2-8-1:串口1收发实验(P3.0和P3.1)”的基础上修改。本节对应的实验源码是:“实验2-11-1:片内EEPROM读写 - 单个字节(STC8A8K32S4A12)”。

3.1.工程需要用到的c文件

本例需要用到的c文件如下表所示,工程需要添加下表中的c文件。

表4:实验需要用到的c文件
序号文件名后缀功能描述
1uart.c包含与用户uart有关的用户自定义函数
2eeprom.cE2PROM有关的用户自定义函数
3delay.c包含用户自定义延时函数

3.2.头文件引用和路径设置

■ 需要引用的头文件

#include "delay.h"  
#include "uart.h"  
#include "eeprom.h"  

■ 需要包含的头文件路径

本例需要包含的头文件路径如下表:

表5:头文件包含路径
序号路径描述
1…\ Sourceuart.h、eeprom.h和delay.h头文件在该路径,所以要包含
2…\UserSTC8.h头文件在该路径,所以要包含

MDK中点击魔术棒,打开工程配置窗口,按照下图所示添加头文件包含路径。

在这里插入图片描述

图9:添加头文件包含路径

3.3.编写代码

首先,在eeprom.c文件中编写操作E2PROM外设会用到的函数,如下表所示。

表6:E2PROM相关用户函数汇集
序号函数名功能描述
1Disable_EEPROM禁止对用户的应用程序区的读、写、擦除操作
2EEPROM_read_n读取指定地址的n个字节数据
3EEPROM_SectorErase擦除指定地址扇区
4EEPROM_write_n写入n个字节数据到指定地址

关于每个E2PROM相关用户函数,下面详细给出代码。

程序清单:禁止访问ISP/IAP函数

 /************************************************************************** 
 * 描  述 : 禁止访问ISP/IAP 
 * 入  参 : 无 
 * 返回值 : 无 
 *************************************************************************/  
void Disable_EEPROM(void)  
{  
    IAP_CONTR = 0x00;           //对ISP/IAP控制寄存器赋值,IAPEN位为0,禁止ISP/IAP操作  
    IAP_CMD   = 0x00;           //清零ISP/IAP命令寄存器各位,去除ISP/IAP命令  
    IAP_TRIG  = 0x00;           //清零ISP/IAP命令触发寄存器各位,防止ISP/IAP命令误触发  
    IAP_ADDRH = 0x80;         //ISP/IAP地址寄存器高8位赋值  
    IAP_ADDRL = 0x00;         //ISP/IAP地址寄存器低8位赋值,指向非EEPROM区,防止误操作  
} 

程序清单:读取指定地址的n个字节数据函数

/*************************************************************************** 
 * 描  述 : 从指定EEPROM首地址读出n个字节放指定的缓冲 
 * 入  参 : EE_address:  读出EEPROM的首地址 
            DataAddress: 读出数据放缓冲的首地址 
            number:      读出的字节长度 
 * 返回值 : 无 
 **************************************************************************/  
void EEPROM_read_n(uint16 EE_address,uint8 *DataAddress,uint16 number)  
{  
      
    EA = 0;                       //禁止总中断  
    IAP_CONTR |= 0x80;          //IAPEN位置0,允许ISP/IAP操作  
    IAP_CONTR |= 0x03;          //WT0和WT1位置1,用于设置CPU等待时间  
    IAP_CONTR &= 0xFB;          //WT2位置0,用于设置CPU等待时间  
    IAP_CMD = 0x01;             //MS0位置1,MS1位置0,对ISP/IAP进行字节读操作  
    do  
    {  
        IAP_ADDRH = EE_address / 256;       //ISP/IAP地址寄存器高8位赋值  
        IAP_ADDRL = EE_address % 256;       //ISP/IAP地址寄存器低8位赋值  
        IAP_TRIG = 0x5A;                              //ISP/IAP命令触发寄赋值为5AH  
        IAP_TRIG = 0xA5;                                //ISP/IAP命令触发寄赋值为A5H                                              
        _nop_();                                  //空命令  
        *DataAddress = IAP_DATA;              //读出ISP/IAP数据寄存器的值送往指定缓存  
        EE_address++;  
        DataAddress++;  
    }while(--number);  
    Disable_EEPROM();             //禁止访问ISP/IAP  
    EA = 1;                         //开启总中断  
} 

程序清单:擦除指定地址扇区函数

/*************************************************************************** 
 * 描  述 : 把指定地址的EEPROM扇区擦除 
 * 入  参 : EE_address:  读出EEPROM的首地址 
            DataAddress: 读出数据放缓冲的首地址. 
            number:      读出的字节长度. 
 * 返回值 : 无 
 **************************************************************************/  
void EEPROM_SectorErase(uint16 EE_address)  
{  
    EA = 0;                           //禁止总中断  
    IAP_ADDRH = EE_address / 256;       //ISP/IAP地址寄存器高8位赋值  
    IAP_ADDRL = EE_address % 256;       //ISP/IAP地址寄存器低8位赋值  
    IAP_CONTR |= 0x80;              //IAPEN位置0,允许ISP/IAP操作  
    IAP_CONTR |= 0x03;              //WT0和WT1位置1,用于设置CPU等待时间  
    IAP_CONTR &= 0xFB;              //WT2位置0,用于设置CPU等待时间  
    IAP_CMD = 0x03;                 //MS0位置1,MS1位置1,对ISP/IAP进行扇区擦除操作  
    IAP_TRIG = 0x5A;                              //ISP/IAP命令触发寄赋值为5AH  
  IAP_TRIG = 0xA5;                              //ISP/IAP命令触发寄赋值为A5H                                              
    _nop_();                                  //空命令  
    Disable_EEPROM();               //禁止访问ISP/IAP  
    EA = 1;                           //重新允许中断  
} 

程序清单:写入n个字节数据到指定地址函数

/*************************************************************************** 
 * 描  述 : 从指定EEPROM首地址读出n个字节放指定的缓冲 
 * 入  参 : EE_address:  写入EEPROM的首地址 
            DataAddress: 写入源数据的缓冲的首地址 
            number:      写入的字节长度 
 * 返回值 : 无 
 **************************************************************************/  
void EEPROM_write_n(uint16 EE_address,uint8 *DataAddress,uint16 number)  
{  
    EA = 0;                       //禁止总中断  
    IAP_CONTR |= 0x80;          //IAPEN位置0,允许ISP/IAP操作  
    IAP_CONTR |= 0x03;          //WT0和WT1位置1,用于设置CPU等待时间  
    IAP_CONTR &= 0xFB;          //WT2位置0,用于设置CPU等待时间  
    IAP_CMD = 0x02;             //MS0位置0,MS1位置1,对ISP/IAP进行字节写操作  
    do  
    {  
        IAP_ADDRH = EE_address / 256;       //ISP/IAP地址寄存器高8位赋值  
        IAP_ADDRL = EE_address % 256;       //ISP/IAP地址寄存器低8位赋值  
        IAP_DATA = *DataAddress;              //将指定缓存数据送往ISP/IAP数据寄存器  
        IAP_TRIG = 0x5A;                              //ISP/IAP命令触发寄赋值为5AH  
        IAP_TRIG = 0xA5;                                //ISP/IAP命令触发寄赋值为A5H                                              
        _nop_();                                  //空命令  
        EE_address++;  
        DataAddress++;  
    }while(--number);  
    Disable_EEPROM();             //禁止访问ISP/IAP  
    EA = 1;                         //开启总中断  
} 

☆注:以上关于E2PROM的用户自定义函数有对EA的操作,即操作E2PROM相关寄存器之前关掉总中断EA,之后再打开总中断EA。这在官方手册提供的例程中并没有,这个仅供用户参考。

然后,在主函数中对串口1进行初始化,通过串口1发送不同的命令实现对单片机片内E2PROM的单字节读、写及扇区擦除等操作。(注意读写的首地址)

代码清单:主函数

int main()  
{  
    P3M1 &= 0xFE;   P3M0 &= 0xFE;                     //设置P3.0为准双向口  
    P3M1 &= 0xFD;   P3M0 |= 0x02;                     //设置P3.1为推挽输出  
      
    Uart1_Init();                                 //串口1初始化    
    EA = 1;                                       //使能总中断  
  delay_ms(10);                                 //初始化后延时  
      
  while (1)  
    {  
     if(WriteFLAG)                        //写模式  
         {  
              WriteFLAG=0;                      //写标志变量清零,发送一次  
              EEPROM_write_n(0x0000,scan,1);        //在EEPROM的首地址为0x0000处写入1个字节  
        SendDataByUart1(0x33);            //串口1发送数据0x33  
         }  
         if(ReadFLAG)                           //读模式  
         {  
              ReadFLAG=0;                         //读标志变量清零,发送一次  
              EEPROM_read_n(0x0000,buffer,1);         //在EEPROM的首地址为0x0000处读取1个字节存入buffer数组中  
        SendStringByUart1_n(buffer,1);      //串口1发送buffer中存的数据  
         }    
         if(ClearFLAG)                          //扇区擦除模式  
         {  
              ClearFLAG=0;                        //清除标志变量清零,发送一次  
              EEPROM_SectorErase(0x0000);           //对EEPROM的首地址为0x0000处的扇区进行扇区擦除  
        SendDataByUart1(0x00);              //串口1发送数据0x00  
         }     
    }  
} 

4.片内EEPROM读写 - 多个字节(STC8A8K32S4A12)

☆注:本节的实验源码是在“实验2-11-1:片内EEPROM读写 - 单个字节(STC8A8K32S4A12)”的基础上修改。本节对应的实验源码是:“实验2-11-2:片内EEPROM读写 - 多个字节(STC8A8K32S4A12)”。

4.1.工程需要用到的c文件

本实验需要用到的头文件以及添加头文件包含路径的方法请参考“实验2-11-1:片内EEPROM读写 - 单个字节(STC8A8K32S4A12)”部分。

4.2.编写代码

在eeprom.c文件中编写操作E2PROM外设会用到的函数,请参考“实验2-11-1:片内EEPROM读写 - 单个字节(STC8A8K32S4A12)”部分。

然后,在主函数中对串口1进行初始化,通过串口1发送不同的命令实现对单片机片内E2PROM的多字节读、写及扇区擦除等操作。(注意读写的首地址)

代码清单:主函数

int main()  
{  
    P3M1 &= 0xFE;   P3M0 &= 0xFE;                     //设置P3.0为准双向口  
    P3M1 &= 0xFD;   P3M0 |= 0x02;                     //设置P3.1为推挽输出  
      
    Uart1_Init();                                 //串口1初始化    
    EA = 1;                                       //使能总中断  
  delay_ms(10);                                 //初始化后延时  
      
  while (1)  
    {  
     if(WriteFLAG)                    //写模式  
         {  
              WriteFLAG=0;                      //写标志变量清零,发送一次  
              EEPROM_write_n(0x0000,scan,5);        //在EEPROM的首地址为0x0000处写入5个字节  
        SendDataByUart1(0x33);            //串口1发送数据0x33  
         }  
         if(ReadFLAG)                     //读模式  
         {  
              ReadFLAG=0;                         //读标志变量清零,发送一次  
              EEPROM_read_n(0x0000,buffer,5);         //在EEPROM的首地址为0x0000处读取5个字节存入buffer数组中  
        SendStringByUart1_n(buffer,5);      //串口1发送buffer中存的数据  
         }    
         if(ClearFLAG)                    //扇区擦除模式  
         {  
              ClearFLAG=0;                        //清除标志变量清零,发送一次  
              EEPROM_SectorErase(0x0000);           //对EEPROM的首地址为0x0000处的扇区进行扇区擦除  
        SendDataByUart1(0x00);              //串口1发送数据0x00  
         }     
    }  
} 

总结

以上就是今天要讲的内容,希望对你有帮助!

  • 3
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

电子友人张

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值