全套cubemx+HAL+UART+DMA+IDLE配置

1、cubemx配置uart+中断

第一步-新建工程

1.选择MCU-stm32f407vgt6
在这里插入图片描述

第二步-配置DMA

2.配置时钟等必配功能后,打开配置串口的DMA如下
在这里插入图片描述

第三步-配置中断

3.配置时钟等必配功能后,打开配置串口的中断如下(这里打开了6个串口,都是相同的配置)
在这里插入图片描述

第四步-生成

4.点击GENERATE CODE直接生成
在这里插入图片描述

2、移植缓冲fifo文件

第一步-添加BSP、APP文件夹

4.在生成的文件夹中添加文件夹BSP、APP,并在其子目录建立source include 两目录如下:
在这里插入图片描述

第二步-准备cfifo.c fifo.h

cfifo.c

#include "cfifo.h"
 
 
/*环形CFIFO初始化*/
void CfifoBuff_Init(CfifoBuff * Cfifo_pointer)
{
   //初始化相关信息
   Cfifo_pointer->Head = 0;
   Cfifo_pointer->Tail = 0;
   Cfifo_pointer->Lenght = 0;
   memset(Cfifo_pointer->BUFF,'\0',CFIFO_SIZE);  //环形CFIFO缓存区初始化
}
 
/*环形CFIFO数据清除*/
void CfifoBuff_Clear(CfifoBuff * Cfifo_pointer)
{
   //清除相关信息
   Cfifo_pointer->Head = 0;
   Cfifo_pointer->Tail = 0;
   Cfifo_pointer->Lenght = 0;
   memset(Cfifo_pointer->BUFF,'\0',CFIFO_SIZE); //环形CFIFO缓存区清除
}
 
 
 
/*环形CFIFO数据写入*/
/*
*参数说明:
*         Cfifo_pointer————环形CFIFO结构体
*         User_buff————待写入数据
*         num————写入数据长度
*
*返回值说明:正确写入到FIFO缓存区中的数据长度
*
*功能说明:将User_buff中的数据写入到环形CFIFO缓存区
*
*/
int16_t CfifoBuff_Write(CfifoBuff * Cfifo_pointer,char * User_buff,uint16_t num)
{
	int16_t i , wrint_num;
	
    if(Cfifo_pointer->Lenght >= CFIFO_SIZE) //判断缓存区是否已满
    {
		 wrint_num=-1;
		 
         return wrint_num;       //数据溢出
    }
		
    if(Cfifo_pointer->Lenght+num<CFIFO_SIZE)  //判断写入的数据长度是否超出当前可写入的最大值
	{
		wrint_num=num;
	}
	else
	{
		wrint_num=CFIFO_SIZE-Cfifo_pointer->Lenght;	  
	}
		
	for(i=0;i<wrint_num;i++)
	{
	    Cfifo_pointer->BUFF[Cfifo_pointer->Tail]=*(User_buff+i);
			
		Cfifo_pointer->Tail = (Cfifo_pointer->Tail+1)%CFIFO_SIZE;//防止越界非法访问
	}	
		
    Cfifo_pointer->Lenght+=wrint_num;
		
    return wrint_num;  //返回正确写入的数据长度
}
 
 
/*环形CFIFO读取*/
/*
*参数说明:
*         Cfifo_pointer————环形CFIFO结构体
*         User_buff————读取数据存放地
*         num————读取数据长度
*
*返回值说明:正确读取到User_buff的数据长度
*
*功能说明:将环形CFIFO缓存区的数据读取到User_buff
*
*/
int16_t CfifoBuff_Read(CfifoBuff * Cfifo_pointer,char * User_buff,uint16_t num)
{
	int16_t i , read_num;
	
    if(Cfifo_pointer->Lenght == 0) //判断非空
    {
        read_num=-1;
		 
        return read_num;       //没有数据
    }	
	
   if(Cfifo_pointer->Lenght-num>=0)       //判断读取的数据长度是否超出当前可读取的最大值
   {
		read_num=num;
   }
   else
   {
    	read_num=Cfifo_pointer->Lenght;	  
   }
		
   for(i=0;i<read_num;i++)
   {
	    *(User_buff+i)=Cfifo_pointer->BUFF[Cfifo_pointer->Head];
			
		Cfifo_pointer->Head = (Cfifo_pointer->Head+1)%CFIFO_SIZE;//防止越界非法访问
   }	
		
    Cfifo_pointer->Lenght-=read_num;
		
    return read_num;  //返回正确写入的数据长度
		
}

cfifo.c

#ifndef _CFIFO_H_
#define _CFIFO_H_
 
#ifdef __cplusplus
extern "C" {
#endif
 
/* Includes ------------------------------------------------------------------*/
#include "main.h"
 
/* USER CODE BEGIN Includes */
#include <string.h>
#include <stdio.h>
/* USER CODE END Includes */
 
/* USER CODE BEGIN Private defines */
 
#define CFIFO_SIZE 1024           //环形队列CFIFO大小
 
 
/*环形CFIFO结构体*/
typedef struct
{
	uint16_t Head;       //环形CFIFO队列头
	uint16_t Tail;	     //环形CFIFO队列尾
    uint16_t Lenght;     //环形CFIFO数据长度
	uint8_t  BUFF[CFIFO_SIZE];     //环形CFIFO缓存区
}CfifoBuff;
 
 
/* USER CODE END Private defines */
 
 
/* USER CODE BEGIN Prototypes */
void CfifoBuff_Init(CfifoBuff * Cfifo_pointer);  //CFIFO初始化
void CfifoBuff_Clear(CfifoBuff * Cfifo_pointer); //CFIFO数据清除
int16_t CfifoBuff_Write(CfifoBuff * Cfifo_pointer,char * User_buff,uint16_t num); //CFIFO数据写人
int16_t CfifoBuff_Read(CfifoBuff * Cfifo_pointer,char * User_buff,uint16_t num); //CFIFO数据读出
/* USER CODE END Prototypes */
 
#ifdef __cplusplus
}
#endif
 
#endif /* _CFIFO_H_ */
 
 

第三步-准备cfifo_usart_dma.c cfifo_usart_dma.h

cfifo_usart_dma.c

#include "cfifo_usart_dma.h" 




static uint8_t DbgTxDma[MW_UART_DMA_LEN] = {0};	//DMA发送缓存区
static uint8_t DbgRxDma[MW_UART_DMA_LEN] = {0}; //DMA接收缓存区

static uint8_t E2KTxDma[MW_UART_DMA_LEN] = {0};	//DMA发送缓存区
static uint8_t E2KRxDma[MW_UART_DMA_LEN] = {0}; //DMA接收缓存区

static uint8_t FPGATxDma[MW_UART_DMA_LEN] = {0};	//DMA发送缓存区
static uint8_t FPGARxDma[MW_UART_DMA_LEN] = {0}; //DMA接收缓存区

MW_UART_ATTR E2K_UartAttr;
MW_UART_ATTR FPGA_UartAttr;
MW_UART_ATTR Dbg_UartAttr; 


/**
  * @brief  初始化串口维护的循环队列
  * @param  sUartAttr  维护串口循环队列的结构体 包括发送接收缓冲fifo、dma的偏移量、发送状态、dma的缓冲大小
  * @param  huart 		维护的串口号 huartx(x = 1.2.3) 在usart.c中定义
  * @param  RxDma 		DMA接收数据缓冲区
  * @param  TxDma 		DMA接收数据缓冲区  
  * @retval 初始化成功状态 成功:MW_FAIL  	失败:MW_SUCCESS 
  */
int8_t MW_UART_Init(MW_UART_ATTR *sUartAttr, UART_HandleTypeDef *huart, uint8_t *RxDma, uint8_t *TxDma)
{ 
		MW_UART_ATTR *pUartAttr = NULL;	
		
		/*为属性的参数附初值 */
		pUartAttr = sUartAttr;
		pUartAttr->handle = huart;
		pUartAttr->DamOffset = 0;
		pUartAttr->TransFlag = MW_TRANS_IDLE;
		pUartAttr->DmaSize = MW_UART_DMA_LEN;
		pUartAttr->pReadDma = RxDma;
		pUartAttr->pWriteDma = TxDma;
		CfifoBuff_Init(&pUartAttr->AcceptCFifo);  //用于数据接受
		CfifoBuff_Init(&pUartAttr->SendCFifo);    //用于数据发送 
		
		/*配置DMA参数并使能中断*/
		if(HAL_OK != HAL_UART_Receive_DMA(pUartAttr->handle, pUartAttr->pReadDma, MW_UART_DMA_LEN))
		{
				return MW_FAIL;
		} 
		/*使能串口空闲中断*/
		__HAL_UART_ENABLE_IT(pUartAttr->handle, UART_IT_IDLE); 
		
		return MW_SUCCESS;
}
/**
  * @brief  初始化所有串口维护的循环队列
  * @param  none  
  * @retval 初始化成功状态 成功:MW_FAIL   失败:MW_SUCCESS 
  */
int8_t MW_UART_Init_All()
{  
		if(MW_UART_Init(&E2K_UartAttr, &huart3, E2KRxDma, E2KTxDma) &&  \
			 MW_UART_Init(&FPGA_UartAttr, &huart2, FPGARxDma, FPGATxDma) &&  \
				MW_UART_Init(&Dbg_UartAttr, &huart1, DbgRxDma, DbgTxDma))
		{
				return MW_SUCCESS;
		}

		return MW_FAIL;
}


/**
  * @brief  使用指令的循环队列发送数据。
  * @param  sUartAttr  维护串口循环队列的结构体 包括发送接收缓冲fifo、dma的偏移量、发送状态、dma的缓冲大小
  * @param  buffer 		发送数据缓冲区地址
  * @param  len 		  发送长度大小 
  * @retval  	发送成功长度大小
  */
int32_t MW_UART_Transmit(MW_UART_ATTR *sUartAttr, uint8_t* buffer,int32_t len)
{
		int32_t TransNum = 0;
		int32_t TransLen = 0;
		MW_UART_ATTR *pUartAttr = sUartAttr;
		/*将要发送的数据先写入循环缓冲区*/
		TransNum = CfifoBuff_Write(&pUartAttr->SendCFifo, (char *) buffer, len);
		
		/*如果发送DMA未在发送中,则使能发送*/
		if(pUartAttr->TransFlag == MW_TRANS_IDLE)
		{
				TransLen = CfifoBuff_Read(&pUartAttr->SendCFifo,(char *)(pUartAttr->pWriteDma),pUartAttr->DmaSize);
				if(TransLen > 0)
				{
						pUartAttr->TransFlag = MW_TRANS_BUSY;
						if(HAL_OK != HAL_UART_Transmit_DMA(pUartAttr->handle,pUartAttr->pWriteDma,TransLen))
						{
								printf("Uart Trans_DMA failed\r\n");
						}
				}				
		} 
		return TransNum;
}
 

cfifo_usart_dma.c

#ifndef _CFIFO_USART_DMA_H_
#define _CFIFO_USART_DMA_H_
 
#ifdef __cplusplus
extern "C" {
#endif
 
/* Includes ------------------------------------------------------------------*/
#include "main.h"
 
/* USER CODE BEGIN Includes */
#include <string.h>
#include <stdio.h>
#include "cfifo.h"
#include "usart.h"
/* USER CODE END Includes */
 
/* USER CODE BEGIN Private defines */
 
#define MW_FAIL              0
#define MW_SUCCESS           1

#define MW_TRANS_IDLE        0
#define MW_TRANS_BUSY        1

#define MW_UART_DMA_LEN      256
 
/* USER CODE END Private defines */
typedef struct 
{ 
	 UART_HandleTypeDef  *handle;    /*HAL库提供的串口句柄*/
	 int16_t TransFlag;              /*数据发送标志位*/      
	 int32_t DmaSize;                /*DMA缓冲区的大小*/
	 int32_t DamOffset;              /*获取数据在DMA缓冲区的偏移量*/
	 uint8_t *pReadDma;              /*指向接收DMA缓冲区的首地址*/
	 uint8_t *pWriteDma;             /*指向发送DMA缓冲区的首地址*/
	 CfifoBuff AcceptCFifo;          /*接受数据的循环缓冲区*/
	 CfifoBuff SendCFifo;            /*发送数据的循环缓冲区*/
}MW_UART_ATTR;	
 

extern MW_UART_ATTR E2K_UartAttr;
extern MW_UART_ATTR FPGA_UartAttr;
extern MW_UART_ATTR Dbg_UartAttr;

 
/* USER CODE BEGIN Prototypes */
/**
  * @brief  初始化所有串口维护的循环队列
  * @param  none  
  * @retval 初始化成功状态 成功:MW_FAIL   失败:MW_SUCCESS 
  */
int8_t MW_UART_Init_All(void);
/**
  * @brief  初始化串口维护的循环队列
  * @param  sUartAttr  维护串口循环队列的结构体 包括发送接收缓冲fifo、dma的偏移量、发送状态、dma的缓冲大小
  * @param  huart 		维护的串口号 huartx(x = 1.2.3) 在usart.c中定义
  * @param  RxDma 		DMA接收数据缓冲区
  * @param  TxDma 		DMA接收数据缓冲区  
  * @retval 初始化成功状态 成功:MW_FAIL  	失败:MW_SUCCESS 
  */
int8_t MW_UART_Init(MW_UART_ATTR *sUartAttr, UART_HandleTypeDef *huart, uint8_t *RxDma, uint8_t *TxDma); 


/**
  * @brief  使用指令的循环队列发送数据。
  * @param  sUartAttr  维护串口循环队列的结构体 包括发送接收缓冲fifo、dma的偏移量、发送状态、dma的缓冲大小
  * @param  buffer 		发送数据缓冲区地址
  * @param  len 		  发送长度大小 
  * @retval  	发送成功长度大小
  */
int32_t MW_UART_Transmit(MW_UART_ATTR *sUartAttr, uint8_t* buffer,int32_t len); 


void MW_UART_IRQHandler(UART_HandleTypeDef *huart);     //串口中断中调用

/* USER CODE END Prototypes */
 
#ifdef __cplusplus
}
#endif
#endif /* _CFIFO_USART_DMA_H_ */
 

3、配置缓冲fifo代码文件具体位置和步骤。

第一步-修改fifo.h

cfifo.h中,改变最多的就是去修改18行的缓冲大小,其他改动不大

第一步-修改cfifo_usart_dma.c

cfifo_usart_dma.中,需要根据你使用的串口多少进行初始化,改变地方如下:

1、根据串口数量,为每个串口的发送接收开启DMA的缓冲区,其实就是静态的全局数组,自行添加减少(6~13行),如下图
在这里插入图片描述

2、根据串口数量,为每个串口的定义一个维护的结构体,如MW_UART_ATTR Dbg_UartAttr; 自行添加减少(15~17行),如下图

在这里插入图片描述

3、根据串口数量,进行初始化绑定, 自行添加减少(60~63行),如下图
在这里插入图片描述

第三步-修改cfifo_usart_dma.h

1、根据cfifo_usart_dma.c定义的维护串口数量,进行extern的外扩到其他文件。 自行添加减少cfifo_usart_dma.h(42~44行),如下图
在这里插入图片描述
至此配置fifo的操作就到此结束了,接下来就是在整个工程的调用和使用了

5、工程配置绑定使用

第一步-修改rewrite_it.c

#include "rewrite_it.h"
#include "usart.h"  



MW_UART_ATTR *CaseUsart(UART_HandleTypeDef *huart)
{
	MW_UART_ATTR *ret = NULL;
	
	if(huart->Instance == huart1.Instance)
	{ 
			ret = &UART0_UartAttr;
	}
	else if(huart->Instance == huart2.Instance)
	{
			ret = &UART2_UartAttr; 
	} 
	else if(huart->Instance == huart4.Instance)
	{
			ret = &FPGA_UartAttr; 
	}
	else if(huart->Instance == huart6.Instance)
	{
			ret = &Dbg_UartAttr; 
	}
	else
	{
			;
	}
 
	return ret;
}
/**
  * @brief  串口发送完成回调函数
  * @param  huart: 串口句柄 .
* @retval 无
*/
void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart)
{
		int32_t TransNum = 0; 
		MW_UART_ATTR *pUartAttr = NULL;
		
		
		pUartAttr = CaseUsart(huart);
	

		/*从发送循环缓冲区中获取数据*/
		TransNum = CfifoBuff_Read(&pUartAttr->SendCFifo,(char *)(pUartAttr->pWriteDma),pUartAttr->DmaSize);
		if(TransNum > 0)
		{		
				if(HAL_OK != HAL_UART_Transmit_DMA(pUartAttr->handle,pUartAttr->pWriteDma,TransNum))
				{
						printf("Uart Trans_DMA failed\r\n");
				}
		}
		else
		{
				pUartAttr->TransFlag = MW_TRANS_IDLE;
		}
 }

/**
  * @brief  串口接收完成回调函数,进行DMA的偏移量复位,当DMA接收完成会调用此函数
  * @param  huart: 串口句柄 .
  * @retval 无
*/
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{			
		int32_t DmaLen = 0;
		int32_t WriteNum = 0; 
		
		MW_UART_ATTR *pUartAttr = NULL;
		
		
		pUartAttr = CaseUsart(huart);
		
		/*计算需要获取数据的长度*/
		DmaLen = pUartAttr->DmaSize - pUartAttr->DamOffset;
		/*将获取的数据存放到数据缓冲区中*/
		WriteNum = CfifoBuff_Write(&pUartAttr->AcceptCFifo,(char *)(pUartAttr->pReadDma + pUartAttr->DamOffset),DmaLen);
		
		if(WriteNum != DmaLen)
		{
				printf("Uart ReadFifo is not enough\r\n");
		}
		/*复位DMA偏移量*/
		pUartAttr->DamOffset = 0; 
}

/**
  * @brief  串口中断调用函数,处理空闲中断数据,将数据写入到缓冲区中,并维护循环队列大小、头尾指针
  * @param  huart: 串口句柄 .
  * @retval 无
*/
void MW_UART_IRQHandler(UART_HandleTypeDef *huart)
{
		int32_t RecvNum = 0;
		int32_t WriteNum = 0;
		int32_t DmaIdleNum = 0; 
		
		MW_UART_ATTR *pUartAttr = NULL;
		
		pUartAttr = CaseUsart(huart);
		 
		if((__HAL_UART_GET_FLAG(huart,UART_FLAG_IDLE) != RESET))
		{
				/*清除空闲中断标识位,重新接受串口空闲中断*/ 
				__HAL_UART_CLEAR_IDLEFLAG(huart);
				
				/*计算在DMA缓冲区需要获取的数据长度*/
				DmaIdleNum = __HAL_DMA_GET_COUNTER(huart->hdmarx);
				RecvNum = pUartAttr->DmaSize - DmaIdleNum - pUartAttr->DamOffset;
				/*将获取到的数据放到数据接收缓冲区中*/                                           
				WriteNum = CfifoBuff_Write(&pUartAttr->AcceptCFifo,(char *)(pUartAttr->pReadDma + pUartAttr->DamOffset),RecvNum);
				
				if(WriteNum != RecvNum)
				{
						printf("Uart ReadFifo is not enough\r\n");
				}
				/*计算获取数据位置的偏移量*/
				pUartAttr->DamOffset += RecvNum;
				//		
				//		printf("%s",pUartAttr.AcceptCFifo.BUFF);
				//		
				//		CfifoBuff_Clear(&pUartAttr.AcceptCFifo);
		}
} 
 

*对于rewrite_it 文件,主要需要修改的地方是MW_UART_ATTR *CaseUsart(UART_HandleTypeDef huart)这个函数,根据串口情况,对维护串口进行选择,根据需要自行修改函数,函数如下图:
在这里插入图片描述

第二步-修改rewrite_it.h

#ifndef _REWRITE_IT_
#define _REWRITE_IT_
 
/**
  * @brief  串口发送完成回调函数
  * @param  huart: 串口句柄 .
* @retval 无
*/
void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart);
/**
  * @brief  串口接收完成回调函数
  * @param  huart: 串口句柄 .
  * @retval 无
*/
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart);
/**
  * @brief  串口中断调用函数,处理空闲中断数据,将数据写入到缓冲区中,并维护循环队列大小、头尾指针
  * @param  huart: 串口句柄 .
  * @retval 无
*/ 
void MW_UART_IRQHandler(UART_HandleTypeDef *huart);

#endif

第三步-修改stm32f4xx_it.c文件

对中断函数UARTx_IRQHandler或USARTx_IRQHandler函数添加MW_UART_IRQHandler(&huartx);
在这里插入图片描述
到此,所有的添加配置都已经完成。

4、使用举例

{
	char sbuf[128]; 
	char sbuf[128]; 
	MW_UART_Transmit(&UART2_UartAttr, (uint8_t *)sbuf, len);
}
  • 21
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
以下是HAL库中使用UART2和DMA的中断配置代码示例: ``` // 定义DMAUART2的句柄 DMA_HandleTypeDef hdma_usart2_rx; UART_HandleTypeDef huart2; // 配置UART2和DMA接收 void MX_USART2_UART_Init(void) { // ... UART2的基本配置 ... // 配置DMA句柄 hdma_usart2_rx.Instance = DMA1_Stream5; hdma_usart2_rx.Init.Channel = DMA_CHANNEL_4; hdma_usart2_rx.Init.Direction = DMA_PERIPH_TO_MEMORY; hdma_usart2_rx.Init.PeriphInc = DMA_PINC_DISABLE; hdma_usart2_rx.Init.MemInc = DMA_MINC_ENABLE; hdma_usart2_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE; hdma_usart2_rx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE; hdma_usart2_rx.Init.Mode = DMA_NORMAL; hdma_usart2_rx.Init.Priority = DMA_PRIORITY_LOW; hdma_usart2_rx.Init.FIFOMode = DMA_FIFOMODE_DISABLE; HAL_DMA_Init(&hdma_usart2_rx); // 关联DMAUART2 __HAL_LINKDMA(&huart2, hdmarx, hdma_usart2_rx); // 使能UART2 IDLE中断 SET_BIT(huart2.Instance->CR1, USART_CR1_IDLEIE); // 使能DMA传输完成中断 __HAL_DMA_ENABLE_IT(&hdma_usart2_rx, DMA_IT_TC); } // UART2中断回调函数 void HAL_UART_IDLE_Callback(UART_HandleTypeDef *huart) { // 如果是UART2触发的中断 if (huart == &huart2) { // 关闭DMA传输 HAL_UART_DMAStop(&huart2); // 处理接收到的数据 // ... // 重新开启DMA传输 HAL_UART_Receive_DMA(&huart2, rx_buf, RX_BUF_SIZE); } } // DMA传输完成中断回调函数 void HAL_DMA_TC_Callback(DMA_HandleTypeDef *hdma) { // 如果是UART2触发的中断 if (hdma == &hdma_usart2_rx) { // 关闭DMA传输 HAL_UART_DMAStop(&huart2); // 处理接收到的数据 // ... // 重新开启DMA传输 HAL_UART_Receive_DMA(&huart2, rx_buf, RX_BUF_SIZE); } } // 启动UART2接收 void start_uart2_receive(void) { HAL_UART_Receive_DMA(&huart2, rx_buf, RX_BUF_SIZE); } ``` 以上代码中,我们定义了一个DMAUART2的句柄,然后在初始化函数中进行了DMAUART2的配置,包括了DMA数据传输方向、传输模式、优先级等参数的设置。接着我们使用了`__HAL_LINKDMA()`函数将DMAUART2关联起来,并使能了UART2的IDLE中断和DMA的传输完成中断。最后我们在中断回调函数中处理接收到的数据,重新开启DMA传输。在程序中我们可以通过调用`start_uart2_receive()`函数启动UART2的接收。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

shuangwei我是伟

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

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

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

打赏作者

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

抵扣说明:

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

余额充值