UHF射频读写器实现对EPCC1-G2电子标签的寻卡,读卡,写卡功能

HX9809U-L超高频读写器:
读写器厂家提供SDK,采用动态链接加载的方式。方式如下`:
1.头文件定义相关变量

    class CUHFDlg : public CDialog
    {
    // Construction
    public:
    
    	CUHFDlg(CWnd* pParent = NULL);	// standard constructor
    
    
    	
    	void addMsg(CString str);//自定义消息添加函数
    
    	HINSTANCE g_hRRLibrary;//串口句柄
    	unsigned char ComAdr;//读写器地址
    	int Frmhandle;//读写器操作句柄
    	BOOL b_Search;//寻卡开启标志位
    	BOOL b_SearchOK;//EPC号码寻卡成功标志位
    	int by_EPClength;//EPC号长度
    	unsigned char EPC[100];//EPC号码 以字符存储  16进制显示单个字符
    	CString str_EPC;//EPC号码字符串
    
    	/******************************定义函数**************************/
    
    	//打开串口
    	typedef int (FAR WINAPI*POpenComPort)(int port, unsigned char *ComAdr,unsigned char baud,int *Frmhandle);
    
    	//关闭串口
        typedef int (FAR WINAPI*PCloseComPort)();
    
    	//寻卡
    	typedef int (FAR WINAPI*PInventory_G2)(unsigned char *ComAdr, unsigned char *EPClenandEPC, 
    											int *Totallen, int *CardNum, int FrmHandle);	
    	//读卡
    	typedef int (FAR WINAPI*PReadCard_G2)(unsigned char *ComAdr, 
                                		unsigned char *EPC, 
                                		unsigned char Mem, 
                                		unsigned char WordPtr, 
                                		unsigned char Num, 
                                		unsigned char *Password, 
                                		unsigned char maskadr, 
                                		unsigned char maskLen, 
                                		unsigned char maskFlag, 
                                		unsigned char *Data, 
                                		unsigned char EPClength, 
                                		int *errorcode, 
                                		int FrmHandle);
    	//写卡
    	typedef int (FAR WINAPI*PWriteCard_G2)(unsigned char *ComAdr, 
    						unsigned char *EPC,
    						unsigned char Mem,
    						unsigned char WordPtr, 
    						unsigned char Writedatalen,
    						unsigned char *WrittenData,
    						unsigned char *Password,
    						unsigned char maskadr,
    						unsigned char maskLen,
    						unsigned char maskFlag,
    						int *WrittenDataNum, 
    						unsigned char EPClength, 
    						int *errorcode, 
    						int FrmHandle);	
    
    	//实例化函数
    		POpenComPort		RR_OpenComPort;
    		PCloseComPort       RR_CloseComPort;
    		PInventory_G2		RR_Inventory_G2;
    		PReadCard_G2        RR_ReadCard_G2;
    		PWriteCard_G2       RR_WriteCard_G2;

// Dialog Data
	//{{AFX_DATA(CUHFDlg)
	enum { IDD = IDD_UHF_DIALOG };
	CListBox	m_ListBox;
	//}}AFX_DATA

	// ClassWizard generated virtual function overrides
	//{{AFX_VIRTUAL(CUHFDlg)
	protected:
	virtual void DoDataExchange(CDataExchange* pDX);	// DDX/DDV support
	//}}AFX_VIRTUAL

// Implementation
protected:
	HICON m_hIcon;

	// Generated message map functions
	//{{AFX_MSG(CUHFDlg)
	virtual BOOL OnInitDialog();
	afx_msg void OnSysCommand(UINT nID, LPARAM lParam);
	afx_msg void OnPaint();
	afx_msg HCURSOR OnQueryDragIcon();
	afx_msg void OnBtnOpenCom();
	afx_msg void OnBtnCloseCom();
	virtual void OnOK();
	virtual void OnCancel();
	afx_msg void OnBtnSearch();
	afx_msg void OnTimer(UINT nIDEvent);
	afx_msg void OnBtnReadcard();
	afx_msg void OnBtnWritecard();
	afx_msg void OnBtnReadEPC();
	//}}AFX_MSG
	DECLARE_MESSAGE_MAP()
};
  1. 加载动态链接库,导出函数

     BOOL CUHFDlg::OnInitDialog()
    

    {
    CDialog::OnInitDialog();

     // Add "About..." menu item to system menu.
    
     // IDM_ABOUTBOX must be in the system command range.
     ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
     ASSERT(IDM_ABOUTBOX < 0xF000);
    
     CMenu* pSysMenu = GetSystemMenu(FALSE);
     if (pSysMenu != NULL)
     {
     	CString strAboutMenu;
     	strAboutMenu.LoadString(IDS_ABOUTBOX);
     	if (!strAboutMenu.IsEmpty())
     	{
     		pSysMenu->AppendMenu(MF_SEPARATOR);
     		pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
     	}
     }
    
     // Set the icon for this dialog.  The framework does this automatically
     //  when the application's main window is not a dialog
     SetIcon(m_hIcon, TRUE);			// Set big icon
     SetIcon(m_hIcon, FALSE);		// Set small icon
     
     // TODO: Add extra initialization here
    
     //初始化控件
     GetDlgItem(IDC_BTN_CLOSECOM)->EnableWindow(FALSE);
     GetDlgItem(IDC_BTN_SEARCH)->EnableWindow(FALSE);
     GetDlgItem(IDC_BTN_READCARD)->EnableWindow(FALSE);
     GetDlgItem(IDC_BTN_WRITECARD)->EnableWindow(FALSE);
     GetDlgItem(IDC_BTN_READEPC)->EnableWindow(FALSE);
    
     //调用UHF动态链接库
     g_hRRLibrary = LoadLibrary("UHFReader09.dll");	
    
     //导出动态链接库函数
     if (g_hRRLibrary ==NULL)
     {
     	addMsg("UHFReader09.dll加载失败");
     	return FALSE;
     }
     addMsg("UHFReader09.dll加载成功");
    
     
     if(!(RR_OpenComPort = (POpenComPort)GetProcAddress(g_hRRLibrary,"OpenComPort")))
     {
     	addMsg("OpenComPort加载失败");
     	return FALSE;
     }
     if(!(RR_CloseComPort = (PCloseComPort)GetProcAddress(g_hRRLibrary,"CloseComPort")))
     {
     	addMsg("CloseComPort加载失败");
     	return FALSE;
     }
     
     if(!(RR_Inventory_G2 = (PInventory_G2)GetProcAddress(g_hRRLibrary,"Inventory_G2")))
     {
     	addMsg("Inventory_G2加载失败");
     	return FALSE;
     }
     if(!(RR_ReadCard_G2 = (PReadCard_G2)GetProcAddress(g_hRRLibrary,"ReadCard_G2")))
     {
     	addMsg("ReadCard_G2加载失败");
     	return FALSE;
     }
     if(!(RR_WriteCard_G2 = (PWriteCard_G2)GetProcAddress(g_hRRLibrary,"WriteCard_G2")))
     {
     	addMsg("WriteCard_G2加载失败");
     	return FALSE;
     }
    
     addMsg("函数导出成功!");
     
    
     return TRUE;  // return TRUE  unless you set the focus to a control
    

    }

3 根据函数指针,调用函数,实现SDK功能。

void CUHFDlg::OnBtnOpenCom() 
{
	// TODO: Add your control notification handler code here

	int port=4;//端口号
	BYTE baud=5; //波特率 0=9600bps 1=19200bps 2=38400bps  4=56000 bps 5=57600 bps 6=115200 bps
	ComAdr=255;//输入输出变量,输入255返回,读写器实际的地址,读写器默认返回0

	int ret = RR_OpenComPort(port, &ComAdr,baud,&Frmhandle);
	if(ret==0)
	{
		addMsg("端口打开成功!");
		GetDlgItem(IDC_BTN_OPENCOM)->EnableWindow(FALSE);
		GetDlgItem(IDC_BTN_SEARCH)->EnableWindow(TRUE);
		GetDlgItem(IDC_BTN_CLOSECOM)->EnableWindow(TRUE);
	}
	else if(ret==0x35)
	{
		addMsg("端口已打开或被占用!");
	}
	else
	{
		addMsg("端口打开失败!");
	}	
}

void CUHFDlg::OnBtnCloseCom() 
{
	// TODO: Add your control notification handler code here
	int ret = RR_CloseComPort();
	if(ret==0)
	{
		addMsg("端口关闭成功!");
		GetDlgItem(IDC_BTN_OPENCOM)->EnableWindow(TRUE);
		GetDlgItem(IDC_BTN_CLOSECOM)->EnableWindow(FALSE);
		GetDlgItem(IDC_BTN_SEARCH)->EnableWindow(FALSE);
		GetDlgItem(IDC_BTN_READCARD)->EnableWindow(FALSE);
		GetDlgItem(IDC_BTN_WRITECARD)->EnableWindow(FALSE);
		GetDlgItem(IDC_BTN_READEPC)->EnableWindow(FALSE);
	}
	else
	{
		addMsg("端口关闭失败!");
	}
	
}

void CUHFDlg::addMsg(CString str)
{
	SYSTEMTIME st; 
	CString strTime;
	GetLocalTime(&st); 
	strTime.Format("%.2d:%.2d:%.2d  ",st.wHour,st.wMinute,st.wSecond);
	
	int count = ((CListBox*)GetDlgItem(IDC_LISTBOX))->GetCount();
	if (count==2000)
	{
		((CListBox*)GetDlgItem(IDC_LISTBOX))->DeleteString(0);
		count--;
	}
	((CListBox*)GetDlgItem(IDC_LISTBOX))->InsertString(count,strTime+str);
	((CListBox*)GetDlgItem(IDC_LISTBOX))->SetCurSel(count);
}
void CUHFDlg::OnBtnSearch() 
{
	b_SearchOK=FALSE;
	if (!b_Search)
	{
		//设置寻卡定时器
		SetTimer(1,100,NULL);
		b_Search=TRUE;
	}
	else
	{
		b_Search=FALSE;
		KillTimer(1);
		addMsg("寻卡结束!");
	}

}

void CUHFDlg::OnTimer(UINT nIDEvent) 
{
	// TODO: Add your message handler code here and/or call default

	   addMsg("寻卡中....");

	   int CardNum=0;//搜寻到的卡数量

	   int fCmdRet = RR_Inventory_G2(&ComAdr,EPC, &by_EPClength, &CardNum, Frmhandle);
	   if (fCmdRet==-1)
	   {
		   addMsg("函数调用失败!");
		   return;
	   }
	   if (fCmdRet==0xFB)
	   {
		   addMsg("无电子标签可操作!");
		   return;
	   }
	   if (fCmdRet==1)
	   {
		   addMsg("询查时间结束前返回!");
	   }
	   if (fCmdRet==2)
	   {
		   addMsg("询查时间结束使得询查退出!");
	   }
	   if (fCmdRet==3)
	   {
		   addMsg("如果读到的标签数量无法在一条消息内传送完,将分多次发送。!");
	   }
	   if (fCmdRet==4)
	   {
		   addMsg("还有电子标签未读取,电子标签数量太多,MCU存储不了!");
	   }
	   if (CardNum=1)
	   {

		   
		   str_EPC.Format("%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",EPC[1],EPC[2],EPC[3],EPC[4],EPC[5],EPC[6],EPC[7],EPC[8],EPC[9],EPC[10],EPC[11],EPC[12]);
		   addMsg("卡号:"+str_EPC);
		   b_Search=FALSE;
		   b_SearchOK=TRUE;
		   KillTimer(1);
		   addMsg("寻卡结束");
		   GetDlgItem(IDC_BTN_READCARD)->EnableWindow(TRUE);
		   GetDlgItem(IDC_BTN_WRITECARD)->EnableWindow(TRUE);
		   GetDlgItem(IDC_BTN_READEPC)->EnableWindow(TRUE);
	   }
	   
	   CDialog::OnTimer(nIDEvent);
}

void CUHFDlg::OnBtnReadEPC() 
{
	// TODO: Add your control notification handler code here
	// TODO: Add your control notification handler code here
	BYTE  fOperEPC[100];指向输入数组变量(输入的是每字节都转化为字符的数据)。是电子标签的EPC号	
	BYTE Mem=0x01;//输入变量,一个字节。选择要读取的存储区要读取的存储区。0x00: 保留区;0x01:EPC存储器;0x02:TID存储器;0x03:用户存储器。
	BYTE WordPtr=0x00;//输入变量,一个字节。指定要读取的字起始地址。0x00 表示从第一个字(第一个16位存储体)开始读,0x01表示从第2个字开始读,依次类推。 
	BYTE Num=0x08;	//输入变量,一个字节。要读取的字的个数。不能设置为0x00,将返回参数错误信息。Num不能超过120,即最多读取120个字。若Num设置为0或者超过了120,将返回参数出错的消息。
	BYTE fPassword[4]={0x0,0x0,0x0,0x0};//指向输入数组变量(输入的是每字节都转化为字符的数据),四个字节,这四个字节是访问密码。
	//32位的访问密码的最高位在PassWord的第一字节(从左往右)的最高位,访问密码最低位在PassWord第四字节的最低位,PassWord的前两个字节放置访问密码的高字。
	BYTE maskadr=0;//输入变量,EPC掩模起始字节地址。
	BYTE maskLen=12;//输入变量,掩模字节数
	BYTE maskFlag=0;//输入变量,掩模使能标记 maskFlag =1:掩模使能;maskFlag =0:掩模禁止;
	BYTE CardData[320];	//Data:指向输出数组变量(输出的是每字节都转化为字符的数据),是从标签中读取的数据
	int Errorcode=-1;//输出变量,一个字节,读写器返回响应状态为0xFC时,返回错误代码。
	BYTE EPClength;//输入变量,一个字节。EPC号的字节长度
	
	//获取EPC号码,如果EPC号码为空,则返回
	if(str_EPC=="")
	{
		addMsg("EPC号码为空");
		GetDlgItem(IDC_BTN_READCARD)->EnableWindow(FALSE);
		GetDlgItem(IDC_BTN_WRITECARD)->EnableWindow(FALSE);
		GetDlgItem(IDC_BTN_READEPC)->EnableWindow(FALSE);
		return;
	}
	EPClength=str_EPC.GetLength()/2;
    for(int i=0;i<EPClength;i++)
	{
		fOperEPC[i]=(BYTE)strtoul(str_EPC.Left(2),NULL,16);
		str_EPC=str_EPC.Right(str_EPC.GetLength()-2);
	}	
	int fCmdRet = RR_ReadCard_G2(&ComAdr, fOperEPC, Mem, WordPtr, Num, fPassword, maskadr, maskLen, maskFlag, CardData, EPClength, &Errorcode, Frmhandle);
	
	if (fCmdRet == 0)
	{
		CString str,s2,temp,temps;
		temps="";
		temp="";
		for(int i=0;i<Num*2;i++)
		{
			temp.Format("%02X",CardData[i]);
			temps=temps+temp;
		}		
		addMsg(temps);
		addMsg("读数据成功!");
	}
	else
	{
		addMsg("读数据失败!");
		CString str_rec;
		str_rec.Format("错误代码:0X%02X",fCmdRet);
		addMsg(str_rec);
	}
	GetDlgItem(IDC_BTN_READCARD)->EnableWindow(FALSE);
	GetDlgItem(IDC_BTN_WRITECARD)->EnableWindow(FALSE);
	GetDlgItem(IDC_BTN_READEPC)->EnableWindow(FALSE);
}

void CUHFDlg::OnBtnReadcard() 
{
	// TODO: Add your control notification handler code here
	// TODO: Add your control notification handler code here
	BYTE  fOperEPC[100];指向输入数组变量(输入的是每字节都转化为字符的数据)。是电子标签的EPC号	
	BYTE Mem=0x03;//输入变量,一个字节。选择要读取的存储区要读取的存储区。0x00: 保留区;0x01:EPC存储器;0x02:TID存储器;0x03:用户存储器。
	BYTE WordPtr=0x00;//输入变量,一个字节。指定要读取的字起始地址。0x00 表示从第一个字(第一个16位存储体)开始读,0x01表示从第2个字开始读,依次类推。 
	BYTE Num=0x07;	//输入变量,一个字节。要读取的字的个数。不能设置为0x00,将返回参数错误信息。Num不能超过120,即最多读取120个字。若Num设置为0或者超过了120,将返回参数出错的消息。
	BYTE fPassword[4]={0x0,0x0,0x0,0x0};//指向输入数组变量(输入的是每字节都转化为字符的数据),四个字节,这四个字节是访问密码。
	//32位的访问密码的最高位在PassWord的第一字节(从左往右)的最高位,访问密码最低位在PassWord第四字节的最低位,PassWord的前两个字节放置访问密码的高字。
	BYTE maskadr=0;//输入变量,EPC掩模起始字节地址。
	BYTE maskLen=12;//输入变量,掩模字节数
	BYTE maskFlag=0;//输入变量,掩模使能标记 maskFlag =1:掩模使能;maskFlag =0:掩模禁止;
	BYTE CardData[320];	//Data:指向输出数组变量(输出的是每字节都转化为字符的数据),是从标签中读取的数据
	int Errorcode=-1;//输出变量,一个字节,读写器返回响应状态为0xFC时,返回错误代码。
	BYTE EPClength;//输入变量,一个字节。EPC号的字节长度
	
	//获取EPC号码,如果EPC号码为空,则返回
	if(str_EPC=="")
	{
		addMsg("EPC号码为空");
		GetDlgItem(IDC_BTN_READCARD)->EnableWindow(FALSE);
		GetDlgItem(IDC_BTN_WRITECARD)->EnableWindow(FALSE);
		GetDlgItem(IDC_BTN_READEPC)->EnableWindow(FALSE);
		return;
	}
	EPClength=str_EPC.GetLength()/2;
    for(int i=0;i<EPClength;i++)
	{
		fOperEPC[i]=(BYTE)strtoul(str_EPC.Left(2),NULL,16);
		str_EPC=str_EPC.Right(str_EPC.GetLength()-2);
	}	
	int fCmdRet = RR_ReadCard_G2(&ComAdr, fOperEPC, Mem, WordPtr, Num, fPassword, maskadr, maskLen, maskFlag, CardData, EPClength, &Errorcode, Frmhandle);
	
	if (fCmdRet == 0)
	{
		CString temp,temps;
		temps="";
		temp="";
		for(int i=0;i<Num*2;i++)
		{
			temp.Format("%02x",CardData[i]);
			temps=temps+temp;
		}	
		addMsg(temps);
		char msg[15]={0};
 		memcpy(&msg,CardData,14);
 		CString str;
		str.Format("%s",msg);
		addMsg(msg);
		addMsg("读数据成功!");

		
	}
	else
	{
		addMsg("读数据失败!");
		CString str_rec;
		str_rec.Format("错误代码:0X%02X",fCmdRet);
		addMsg(str_rec);
	}
	GetDlgItem(IDC_BTN_READCARD)->EnableWindow(FALSE);
	GetDlgItem(IDC_BTN_WRITECARD)->EnableWindow(FALSE);
	GetDlgItem(IDC_BTN_READEPC)->EnableWindow(FALSE);
}

void CUHFDlg::OnBtnWritecard() 
{
	// TODO: Add your control notification handler code here
	BYTE  fOperEPC[100];//指向输入数组变量(输入的是每字节都转化为字符的数据)。是电子标签的EPC号。
	BYTE Mem=0x03;//输入变量,一个字节。选择要读取的存储区要读取的存储区。0x00: 保留区;0x01:EPC存储器;0x02:TID存储器;0x03:用户存储器。
	BYTE WordPtr=0x00;//输入变量,一个字节。指定要写入的字起始地址。指定要写入数据的起始地址。如果写的是EPC区,则会忽略这个起始地址。EPC区总是规定从EPC区0x02地址(EPC号的第一个字节)开始写。
	BYTE Writedatalen=14;	//输入变量,一个字节。待写入的字节数(长度必须为偶数字节数)。Writedatalen必须大于0,这里字节数必须和实际待写入的数据个数相等。否则将会返回参数错误的消息。
	BYTE CardData[320];	//指向输入数组变量(输入的是每字节都转化为字符的数据)。待写入的字。这是要写入到存储区的数据。比如,WordPtr等于0x02,则输出变量Data中第一个字(从左边起)写在Mem指定的存储区的地址0x02中,第二个字写在0x03中,依次类推。
	BYTE fPassword[4]={0x0,0x0,0x0,0x0};//指向输入数组变量(输入的是每字节都转化为字符的数据),四个字节,这四个字节是访问密码。
										//32位的访问密码的最高位在PassWord的第一字节(从左往右)的最高位,访问密码最低位在PassWord第四字节的最低位,PassWord的前两个字节放置访问密码的高字。
	BYTE maskadr=0;//输入变量,EPC掩模起始字节地址。
	BYTE maskLen=0;//输入变量,掩模字节数
	BYTE maskFlag=0;//输入变量,掩模使能标记 maskFlag =1:掩模使能;maskFlag =0:掩模禁止;
	int Errorcode=-1;//输出变量,一个字节,读写器返回响应状态为0xFC时,返回错误代码。
	int WritedataNum;//输出变量,已经写入的字的个数。(以字为单位)
	BYTE EPClength;//输入变量,一个字节。EPC号的字节长度
	//获取EPC号码,如果EPC号码为空,则返回
	if(str_EPC=="")
	{
		addMsg("EPC号码为空");
		GetDlgItem(IDC_BTN_READCARD)->EnableWindow(FALSE);
		GetDlgItem(IDC_BTN_WRITECARD)->EnableWindow(FALSE);
		GetDlgItem(IDC_BTN_READEPC)->EnableWindow(FALSE);
		return;
	}
	EPClength=str_EPC.GetLength()/2;
    for(int i=0;i<EPClength;i++)
	{
		fOperEPC[i]=(BYTE)strtoul(str_EPC.Left(2),NULL,16);
		str_EPC=str_EPC.Right(str_EPC.GetLength()-2);
	}
	CString str1,str2,str3,str_ALL;
	//获取写入的内容
	GetDlgItemText(IDC_EDIT_NUMBER,str1);
	GetDlgItemText(IDC_EDIT_NAME,str2);
	GetDlgItemText(IDC_EDIT_VALUE,str3);
	str_ALL=str1+str2+str3;
	if((str_ALL.GetLength())!=14)
	{
		MessageBox("字符串长度错误!",NULL,MB_OK);
		GetDlgItem(IDC_BTN_READCARD)->EnableWindow(FALSE);
		GetDlgItem(IDC_BTN_WRITECARD)->EnableWindow(FALSE);
		GetDlgItem(IDC_BTN_READEPC)->EnableWindow(FALSE);
		return;
	}
	memset(&CardData,0,sizeof(CardData));
	memcpy(&CardData,str_ALL,str_ALL.GetLength());

	//写入数据
	int fCmdRet =  RR_WriteCard_G2(&ComAdr, fOperEPC, Mem, WordPtr, Writedatalen, CardData, fPassword, maskadr, maskLen, maskFlag, &WritedataNum, EPClength, &Errorcode, Frmhandle);	
	if (fCmdRet == 0)
	{

		addMsg("写数据成功!");
	}
	else
	{
		addMsg("写数据失败!");
		CString str_rec;
		str_rec.Format("错误代码:0X%02X",fCmdRet);
		addMsg(str_rec);
	}
	GetDlgItem(IDC_BTN_READCARD)->EnableWindow(FALSE);
	GetDlgItem(IDC_BTN_WRITECARD)->EnableWindow(FALSE);
	GetDlgItem(IDC_BTN_READEPC)->EnableWindow(FALSE);
	
}

二.EPCC1-G2电子标签相关知识
电子标签内部有四个功能区:

  1. 保留区 4字 WORD[01]=8位16进制灭活密码WORD[23]=8位16进制读写密码;
  2. EPC区 WORD[2~7]=EPC号码,标签读写需根据EPC号码来操作;
  3. TID区 WORD[2~5]=16进制全球唯一ID 只读
  4. 用户区 WORD[0~31] 储存用户数据
  • 3
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值