【蓝桥杯嵌入式】五、省赛一条龙(一)全部功能总结汇总

目录

一、需求分析

省赛涉及的外设和可能的考点:

二、通用步骤

设计流程:

建立项目的步骤:

include操作:

定义:

关于中断:

三、可能用到的配置和函数:

LED:

KEY:

​编辑

I2C:

Base TIM:

PWM输入:

PWM输出:

ADC:

RTC:

USART1:

四、容易出错的点


一、需求分析

        建立一套适用于尽量多的蓝桥杯嵌入式项目的方法,包括从建立项目开始、使用开发板上的全部外设到完成比赛。由于比赛利用了CubeMX开发方式,那么我们的思维也要符合CubeMX。按照CubeMX的思想,我们只要记住整体步骤,到比赛时进行剪裁即可。

省赛涉及的外设和可能的考点:

GPIO8个LED、4个按键
会提供驱动程序LCD屏幕、I2C通信和可编程电阻
通用定时器的PWM输入和输出2个频率输入
ADC2个电压采集、读可编程电阻电压值
RTC时间显示和闹钟
USART1与上位机通信
定时器融合在其他考点中
NVIC

融合在其他考点中

二、通用步骤

设计流程:

  1. 浏览一遍题目,有一个整体印象。
  2. 仔细读题目,综合考虑如何配置外设和实现功能。
    1. 确定需要的外设和功能。约定圆形为外设分析、三角型为功能分析,图形中的数字来区分不同的模块。
  3. 设计流程,主循环前做什么、主循环中做什么。
    1. 确定主循环前打开需要的外设
    2. 根据题目一步一步的设计流程,不要遗漏,字不要写的太大,而且要留有足够的空间用来修改。
      1. 流程包括一条主线任务,调用函数就是支线任务。
    3. 同时在另一张草稿纸上写下需要的变量,按照外设或题目中的信息命名,要有特征性和区分度。
  4. 这样设计下来最多1小时,按照设计配置外设和写程序最多2小时,检查和优化1小时,最多4个小时就完成了。
  5. 全局变量定义:

    经验规则:

    标志变量:uint8_t ,只取个位数或0,1

    与计算有关或者需要显示:uint32_t

    浮点型:float

  6. 检查和优化:

    1. 备份项目

    2. 仔细根据题目,检查功能是否符合题目描述,然后备份项目

    3. 将代码中有警告部分想办法修复,将某些变量的类型缩小,减小内存占用。

建立项目的步骤:

【蓝桥杯嵌入式】三、基本操作(一)工具配置-CSDN博客

注意:

TIM、ADC、USART有多个通道,需要选择引脚做配置!

打开MDK后,先编译一下,建立文件之间的关联

include操作:

main.h中的对应代码段进行引入文件。

/* USER CODE BEGIN Includes */
#include "lcd.h"
#include <stdio.h>     //LCD用
#include <string.h>    //USART用
/* USER CODE END Includes */

定义:

变量:

建议全部定义为全局变量,因为用到的变量不多,而且大多数都需要全局使用,这样便于管理。

方法:在main.c中定义变量,然后在main.h中使用extern语句将其改为全局变量。

宏:

main.h中定义宏。

函数:

main.cstm32g4xx_it.c中的对应代码段中实现函数,需要全局使用的函数需要在main.h中定义。

关于中断:

系统时钟中断要设置为0,所有自己打开的中断优先级不能为0(一般设置为1),防止冲突。

所有的中断相关的函数和操作都在stm32g4xx_it.c中进行,中断服务函数的框架在该文件的最下面的区域。 

中断回调函数全部在stm32g4xx_it.c中最下面的用户代码段重新实现。

三、可能用到的配置和函数:

LED:

【蓝桥杯嵌入式】四、各种外设驱动(一)LED-天狼IoT

根据原理图,选择PC8~PC15引脚控制LED,PD2引脚控制锁存器。

  •  LED控制引脚配置为:推挽输出。
  • PD2引脚配置为:推挽输出,低电平。
  • 添加用户标签。

直接在main.c的用户代码段实现下面的函数,可以用  " | " ,连接多个LED_Pin实现多个灯一起操作。

void LED_switch(uint16_t GPIO_Pin,GPIO_PinState PinState)
{
	HAL_GPIO_WritePin(LED_LE_GPIO_Port,LED_LE_Pin,GPIO_PIN_SET);
    HAL_GPIO_WritePin(GPIOC,LED1_Pin|LED2_Pin|LED3_Pin|LED4_Pin|LED5_Pin|LED6_Pin|LED7_Pin|LED8_Pin,GPIO_PIN_SET);
	HAL_GPIO_WritePin(GPIOC,GPIO_Pin,PinState);
	HAL_GPIO_WritePin(LED_LE_GPIO_Port,LED_LE_Pin,GPIO_PIN_RESET);
}

KEY:

【蓝桥杯嵌入式】四、各种外设驱动(二)KEY-天狼IoT

PB0~PB2、PA0

驱动函数:可以直接在main.c里写。

软件去抖动延时90。

//按键1响应函数
void B1Fun()
{
}
 
//按键2响应函数
void B2Fun()
{
}
 
//按键3响应函数
void B3Fun()
{
}
 
//按键4响应函数
void B4Fun()
{
}
 
 
//按键扫描
void KeyScan()
{
	uint32_t keyDelay=90;
	if(HAL_GPIO_ReadPin(B1_GPIO_Port,B1_Pin)==GPIO_PIN_RESET)
	{
		HAL_Delay(keyDelay);
		if(HAL_GPIO_ReadPin(B1_GPIO_Port,B1_Pin)==GPIO_PIN_RESET)
		{
			B1Fun();
		}
	}
	
	if(HAL_GPIO_ReadPin(B2_GPIO_Port,B2_Pin)==GPIO_PIN_RESET)
	{
		HAL_Delay(keyDelay);
		if(HAL_GPIO_ReadPin(B2_GPIO_Port,B2_Pin)==GPIO_PIN_RESET)
		{
			B2Fun();
		}
	}
	
	if(HAL_GPIO_ReadPin(B3_GPIO_Port,B3_Pin)==GPIO_PIN_RESET)
	{
		HAL_Delay(keyDelay);
		if(HAL_GPIO_ReadPin(B3_GPIO_Port,B3_Pin)==GPIO_PIN_RESET)
		{
			B3Fun();
		}
	}
	
	if(HAL_GPIO_ReadPin(B4_GPIO_Port,B4_Pin)==GPIO_PIN_RESET)
	{
		HAL_Delay(keyDelay);
		if(HAL_GPIO_ReadPin(B4_GPIO_Port,B4_Pin)==GPIO_PIN_RESET)
		{
			B4Fun();
		}
	}
}

 LCD:

蓝桥杯嵌入式】四、各种外设驱动(四)LCD驱动-天狼IoT

在LCD上显示其他格式的数据或者需要格式化字符串,要用到<stdio.h>库中的 sprintf 函数:

char LCD_Str[20];
sprintf(LCD_Str,"Num = %d",666);

在main函数中初始化HAL库之后,就初始化LCD、设置颜色、用背景色清屏。因为中断会干扰LCD初始化。

  /* USER CODE BEGIN Init */
	LCD_Init();
	LCD_SetBackColor(Black);
	LCD_SetTextColor(White);
	LCD_Clear(Black);
  /* USER CODE END Init */

最常用的就是写字符串函数和清屏:(注意:需要手动添加空格)

void LCD_DisplayStringLine(u8 Line, u8 *ptr);
void LCD_Clear(u16 Color);

I2C:

 【蓝桥杯嵌入式】四、各种外设驱动(十)I2C通信和可编程电阻的操作-天狼IoT

I2C驱动函数:(有些提供的函数名不同,但功能相同)

操作方法:

//初始化代码段
I2CInit();
char wBuf[5]={0};        //写数据缓冲区、有时需要定义为全局变量
char rBuf[5]={0};        //读数据缓冲区、有时需要定义为全局变量
//初始化代码段


//读取芯片值
iic_24c02_read(rBuf,1,5);    //在芯片的1区,读5个单位数据rBuf

//读电阻设置值
RES_4017=read_resistor();

//写入芯片的操作
sprintf(wBuf,"%d",num);       //复制数据,并将类型转换为字符串
iic_24c02_write(wBuf,1,5);    //在芯片的1区,写5个单位数据wBuf
HAL_Delay(5);                 //存储芯片的写入操作需要时间,这里必须延时
		
//写电阻设置值
write_resistor(num);

Base TIM:

【蓝桥杯嵌入式】四、各种外设驱动(五)基础定时器-天狼IoT

周期定时模式:

  • Prescaler 要设置为X((X+1)=APB1 Timer/(Counter Period+1) ,由于从0开始,所以这里实际上是X+1)
  • Counter Period ,根据需要计算。
  • 如果用到定时器中断服务函数,则需要在NVIC中打开中断,否则不用打开中断,如果不清楚就打开中断。

MDK代码:以TIM6为例

        两种常用的进入主循环前的函数:

//以中断方式启动定时器,一般在独立使用定时器时使用
HAL_TIM_Base_Start_IT(&htim6);

//软件启动定时器,一般在用于触发其他中断时使用
HAL_TIM_Base_Start(&htim6);

         中断服务函数名称:

void TIM6_DAC_IRQHandler(void)

PWM输入:

【蓝桥杯嵌入式】四、各种外设驱动(六)通用定时器(2):利用定时器输入捕获模式读取PWM波(常考)_天狼IoT

引脚对应:

  • TIM3_CH1/TIM16_CH1——PB4——频率输出1
  • TIM2_CH1/TIM8_CH1——PA15——频率输出2

CubeMX设置:

        将Combined Channels(结合渠道)设置为PWM Input on CH1模式

        定时器基本参数:

  • 预分频系数:Prescaler
  • 定时器周期:Counter Period

        定时器模式参数:

  • 捕获极性:Polarity Selection  (根据要读取的PWM波形选择,一般习惯是:如果PWM波以上跳沿开始那么CH1就设置为上跳沿有效)
  • 打开对应的定时器中断,优先级为1。

MDK代码:

        进入主循环前函数:

HAL_TIM_Base_Start(&htim3);
HAL_TIM_IC_Start_IT(&htim3,TIM_CHANNEL_1);
HAL_TIM_IC_Start_IT(&htim3,TIM_CHANNEL_2);

         中断服务函数名称:

void TIM3_IRQHandler(void)

        读取比较寄存器的值:

//读取周期值
uint16_t IC1_width = __HAL_TIM_GET_COMPARE(&htim3,TIM_CHANNEL_1);

//读取占空值
uint16_t IC2_pluse = __HAL_TIM_GET_COMPARE(&htim3,TIM_CHANNEL_2);	

频率计算公式:

PWM波的频率 = 定时器频率/CH1读取的值

定时器频率 = APB1 Timer/(预分频系数+1);

PWM输出:

CubeMX设置:

PWM输出很简单,只需要记住CubeMX中4个具体量的设置,以及使用三个函数就可以玩转PWM输出。

配置:

  • 通过设置 Prescaler             来设置定时器频率 
  • 通过设置 Counter Period   来设置周期值
  • 通过设置 Pulse                   来设置占空值
  • 通过设置 CH Polarity         来设置极性
  • 根据题目确定配置,如果要配置%的占空比,周期就设置为100的倍数。然后根据题目和便于计算的原则,设置预分频系数。

MDK代码:

进入主循环前函数:

//启动对应定时器和通道
HAL_TIM_Base_Start_IT(&htim16);    
HAL_TIM_PWM_Start_IT(&htim16,TIM_CHANNEL_1);

在程序中修改预分频系数、周期、占空比的函数如下,注意差1性。

__HAL_TIM_SET_PRESCALER(设备句柄地址,预分频系数)  //设置预分频系数0~65535

__HAL_TIM_SetAutoreload(设备句柄地址,计数值)    //设置溢出值,即周期

__HAL_TIM_SET_COMPARE(设备句柄地址,通道号,脉宽值);    //设置占空比

PWM波的频率设置计算公式:

周期 =( 定时器频率/PWM波的频率)-1;

定时器频率 = APB1 Timer/ (预分频系数+1);

如:APB1 Timer为80MHz时,设置PWM的频率为8KHz:

由于APB1 Timer=80MHz,周期设置为99

所以

99 =( 定时器频率/8KHz)-1; →定时器频率=800KHz

800KHz =80MHz/(预分频系数+1);→预分频系数=99

ADC:

【蓝桥杯嵌入式】四、各种外设驱动(九)ADC(1):软件触发与中断触发方式-天狼IoT

【蓝桥杯嵌入式】四、各种外设驱动(九)ADC(2):双ADC同步转换方式-天狼IoT

引脚对应:

  • ADC1_IN11——PB12——R38(电压采集2)
  • ADC2_IN15——PB15——R37(电压采集1)

CubeMX设置:

  • ADC Mode:选择需要的通道,选择single_ended
  • Resolution:转换精度,一般保存默认值12
  • External Trigger Conversion Source:中断触发方式
  • External Trigger Conversion Edge:外部触发时使用的信号沿
  • Rank里的Sampling Time:采样时间,不设置也行,建议比赛时为提高准确性设置为640.5。
  • 打开ADC中断,优先级为1,无需打开对应的TIM的中断。

同步转换模式需额外设置:

先打开ADC2_IN15和ADC1_IN11,均选择single_ended模式。

在ADC1_IN11的配置中

  • ADCs_Common_Settings设置为Dual regular simultaneous mode only(双规则同步转换模式)
  • DMA Access Mode(DMA接入方式)设置为DMA access mode enable
  • DMA Continuous Requests:是否连续产生DMA请求
  • External Trigger Conversion Source:设置为Timer x Trigger Out event,使用定时器TIMx产生的UEV中断触发ADC转换。
  • DMA Settings:

        ADC1只有一个DMA请求,为这个DMA请求配置DMA 通道(任意通道都行),设置DMA 传输属性参数,在DMA Request Settings组中将Mode(工作模式)设置为Circular(循环模式),将外设和存储器的数据宽度都设置为Word

  • 打开对应定时器
  • ADC2一般不用再做设置,但要注意:

        ADC2除了Rank通道号外的其他可配置参数参赛都要和ADC1一致,以保证两个ADC能同步采集。 

MDK代码: 

进入主循环前函数:

//独立模式
HAL_ADC_Start_IT(&hadc1);//以中断方式启动ADC

HAL_ADC_Start(&hadc2);//以软件方式启动ADC

//同步模式
HAL_ADCEx_MultiModeStart_DMA(&hadc1,dmaDataBuffer,DATA_LEN);//以DMA方式启动ADC1
HAL_ADCEx_MultiModeStart_DMA(&hadc2,dmaDataBuffer,DATA_LEN);//以DMA方式启动ADC2

中断回调函数:

//同步模式下可以使用中断服务函数,也可以使用回调函数
void DMA1_Channel1_IRQHandler(void)

//独立模式下必须区分是哪个ADC引发中断,必须使用回调函数
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef *hadc);    

 ADC功能函数:

HAL_ADC_PollForConversion(&hadc1,200);//查看ADC的转换是否完成

HAL_ADC_GetValue(&hadc1);//读取ADC转换后的结果并返回

 转换结果电压计算公式:

实际电压值(mV)=ADC读取值*VREF(mV)/2^(转换精度)

VREF是ADC转换的参考电压,开发板上理论是3300mV;转换精度可以在CubeMX中设置,默认为12;

在代码中描述如下:

uint32_t Volt=3300*HAL_ADC_GetValue(&hadc2);
Volt = Volt>>12;

 同步模式下的数据读取:

双ADC同步转换方式中低16位是ADC1的数据,高16位是ADC2 的数据。为得到各自的数据需要做如下处理:

uint32_t adcValue=dmaDataBuffer[0];	//自定义变量dmaDataBuffer是DMA读取数据的缓冲区
	
uint32_t ADC1_val=adcValue&0x0000FFFF;

uint32_t ADC2_val=(adcValue&0xFFFF0000)>>16;

常用方式:

if(HAL_ADC_PollForConversion(&hadc1,200)==HAL_OK)//查看ADC的转换是否完成
{
    uint32_t Volt=3300*HAL_ADC_GetValue(&hadc2);
    Volt = Volt>>12;
}

RTC:

【蓝桥杯嵌入式】四、各种外设驱动(七)RTC实时时钟--时间显示、周期唤醒和闹钟-天狼IoT

周期唤醒与闹钟是两个独立的功能,可以分开使用。

CubeMX设置:

时钟配置界面的左上角有RTC的时钟配置,使用LSI时钟。

1、RTC基本参数设置:

  • Hour Format,小时格式,可以选择12 小时制或24小时制。
  • Output Polarity,输出极性。闹钟A、闹钟B、周期唤醒中断事件信号有效时的输出极性,可设置为高电平或低电平。

        Calendar TimeCalendar Data 分组里有用于设置日历的时间参数和初始化数据的如下参数:

  • Data Format,数据格式,可选择二进制格式或BCD格式,这里选择Binary data forms
  • 初始化时间数据,包括时、分、秒的数据。
  • 初始化日期数据,包括年、月、日的数据。

2、Alarm 闹钟设置:

  • 设置闹钟时间数据,包括时、分、秒的数据。
  • Mask项:设置为Enable表示屏蔽某时间值对闹钟的影响。

3、周期唤醒设置

  • Wake Up Clock 可以设置为各种分频信号,一般设置为1Hz,即每秒一次。
  • Wake Up Counter,周期唤醒重载值,有差一性,设置为0表示值为1。

4、中断设置

RTC的中断唤醒和闹钟的中断的中断优先级都设置为1。

MDK代码:

 中断服务函数/回调函数名称:

RTC_WKUP_IRQHandler(void);

HAL_RTC_AlarmAEventCallback(hrtc);//对应Alarm A

HAL_RTCEx_AlarmBEventCallback(hrtc);//对应Alarm B

RTC函数名称:

 注意:这两个函数必须一起使用,不管你用不用的着,因为必须取出全部结果才可以重新写入。

HAL_RTC_GetTime(&hrtc,&sTime,RTC_FORMAT_BIN);//获取当前时间
HAL_RTC_GetDate(&hrtc,&sData,RTC_FORMAT_BIN);//获取当前日期

时间显示常用框架:

  /* USER CODE END RTC_WKUP_IRQn 0 */
  HAL_RTCEx_WakeUpTimerIRQHandler(&hrtc);
  /* USER CODE BEGIN RTC_WKUP_IRQn 1 */
	RTC_TimeTypeDef sTime;
	RTC_DateTypeDef sData;
	
	if(HAL_RTC_GetTime(&hrtc,&sTime,RTC_FORMAT_BIN) == HAL_OK)
	{
		HAL_RTC_GetDate(&hrtc,&sData,RTC_FORMAT_BIN);
		char str[20];
		sprintf(str,"Data:20%2d-%2d-%2d",sData.Year,sData.Month,sData.Date);
		LCD_DisplayStringLine(Line3,str);
		sprintf(str,"Time:%2d:%2d:%2d",sTime.Hours,sTime.Minutes,sTime.Seconds);
		LCD_DisplayStringLine(Line5,str);
		}
  /* USER CODE END RTC_WKUP_IRQn 1 */

USART1:

【蓝桥杯嵌入式】四、各种外设驱动(八)USART+DMA通信方式和串口通信协议的设计与使用-天狼IoT

注意:

!!容易漏掉的步骤:选择PA9和PA10作为USART1的信号引脚

使用串口的时候有时要关闭MDK,因为会产生干扰。

CubeMX设置:

USART:

  • Mode:工作模式。一般设置为Asynchronous(异步);
  • Baud Rate:波特率。CubeMX默认为115200bit/s,一般题目要求设置成9600bit/s。
  • Data Direction:数据传输方向,保持默认的双向传输。

DMA:

  • Add两个DMA通道,会自动设置通道号和传输方向;将RX的优先级设置为中优先级,模式设置为循环模式;TX保持默认。

打开USART1的中断设置优先级为1,两个DMA通道的中断会自动打开,优先级设置为1。

MDK代码:

进入主循环前函数:

//rxBuf:接收数据缓冲区; cmdlen:接收数据的大小
HAL_UART_Receive_DMA(&huart1,rxBuf ,cmdlen);

 中断服务函数:

//RX对应的通道的中断服务函数,这里是通道1
DMA1_Channel1_IRQHandler(void)

 USART函数:

//txBuffer:要发送的数据指针
HAL_UART_Transmit_DMA(&huart1 ,txBuf ,strlen(txBuffer));

注意:

向上位机发送数据时,有时需要在最后加上 \n  来显示换行。

自定义的接收数据解析函数:

根据题目要求,这里提供一个参考例子:

先简单列个表:

上位机向开发板发送数据的格式定义(xx表示两个位数的数字)
上位机发送的指令字符串指令功能
#Hxx;设置小时,将RTC时间的小时改为xx
#Mxx;设置分钟,将RTC时间的分钟改为xx
#Sxx;设置秒,将RTC时间的秒改为xx
#U01;或#U00;设置上传数据,或不上传数据

然后分解指令,数字类型要减去字符 '0' :

/* USER CODE BEGIN 1 */
void updataRTCTime(void)
{
	if(proBuffer[0] != '#')
		return;
	uint8_t timeSection=proBuffer[1];
	uint8_t tmp10=proBuffer[2]-'0';
	uint8_t tmp1=proBuffer[3]-'0';
	uint8_t val=10*tmp10+tmp1;
	if(timeSection == 'U')
	{
		isUploadTime=val;
		return ;
	}
	RTC_TimeTypeDef sTime;
	RTC_DateTypeDef sDate;
	if(HAL_RTC_GetTime(&hrtc ,&sTime ,RTC_FORMAT_BIN)==HAL_OK )
	{
		HAL_RTC_GetDate(&hrtc ,&sDate ,RTC_FORMAT_BIN );
		if(timeSection == 'H')
			sTime.Hours = val;
		else if (timeSection == 'M')
			sTime.Minutes = val;
		else if (timeSection == 'S')
			sTime.Seconds = val;
		HAL_RTC_SetTime(&hrtc ,&sTime ,RTC_FORMAT_BIN );
	}
}
/* USER CODE END 1 */

四、容易出错的点

  1. 不能总是清屏,应该改成在按下按键或方式特定事件后清屏;
  2. 按键延时就设置为90就行,不快也不慢;
  3. 如果开发板反复无常,就是硬件有问题,要求更换!
  4. 在会引起值减小的位置添加清屏,或者在写LCD显示函数的时候格式化数字的位数。
  5. 计算的步骤要从简
  6. 关于时间的操作可以在TIM中断服务函数中进行的,就不要拿出来
  7. 要使用定时器中断服务函数,就必须用中断方式打开或关闭定时器!
  8. PWM输出功能,需要在进入主循环前打开对应定时器、打开PWM输出!
  9. PWM输入功能,需要在进入主循环前打开对应定时器,还有对应的两个通道!
  10. 我发现题目没有让我们交.ioc文件,所以我们应该把所有的Src和Inc中的文件都提交。
  11. 串口
    1. 接收缓冲区的大小需要用sizeof()来确定,一般题目设置为定值即可。
    2. 发送一般需要加换行符,所以发送缓冲区的大小需要用strlen()来确定。
    3. 发送缓冲区要设置为一个有足够且大小的确定的数组。
    4. 串口接收函数要在处理前延时50ms。
  12. LED的闪烁问题,不可以使用Toggle函数;因为LCD会对引脚的状态产生影响。应该使用标志变量,或利用奇偶数来实现状态切换。
  13. 不知道为什么,我的开发板上一打开TIM就会产生一次超时中断;解决方法是设置一个计数变量为0,只要调用中断服务函数就自增1,当计数为2时再实现计划的功能。
  14. 在LED驱动函数中包含关闭所有LED灯的操作,只需要在进入while前关闭一次LED即可。
  15. 有时需要在中断中清屏,但是中断的位置不确定;所以要设置一个全局标志,在中断改变标志,然后在需要清屏的地方判断标志位来清屏。
  16. LCD驱动函数的Line是从0开始的而题目要求中写的行是从1开开始的。
  17. 发送的字符较长时,发现实际字符长度和理论有区别,比赛时要注意,接收缓冲区要略大于理论长度,可以根据串口调试助手的接收结果开进行调整。
  18. 做题过程中被卡住又无法解决,就果断跳过!要仔细检查,其实不少小而简单的问题可能没有被解决。
  • 23
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值