【FPGA零基础学习之旅#9】状态机基础知识

🎉欢迎来到FPGA专栏~状态机基础知识


  • ☆* o(≧▽≦)o *☆~我是小夏与酒🍹
  • 博客主页:小夏与酒的博客
  • 🎈该系列文章专栏:FPGA学习之旅
  • 文章作者技术和水平有限,如果文中出现错误,希望大家能指正🙏
  • 📜 欢迎大家关注! ❤️
    FPGQ2

CSDN

遇见未来

一、效果演示

🔸Hello状态机例程
RTL视图:
RTL1
状态转移:
状态转移1

🔸 流水灯状态机例程
使用小精灵V2实现的效果:
流水灯
小精灵V2基础使用记录:【FPGA-Spirit_V2】小精灵V2开发板初使用
RTL视图:
RTL2状态转移:
状态转移2

🔸循迹小车pwm状态机例程
pwm

二、状态机基础知识

状态机全称是有限状态机(Finite State Machine,FSM),是表示有限个状态以及在这些状态之间的转移和动作等行为的数学模型。

状态机分为摩尔(Moore)型有限状态机米利(Mealy)型有限状态机。摩尔状态机的输出只由输入确定(不直接依赖于当前状态)。米利有限状态机的输出不止与其输入有关,还与它的当前状态相关,这也是与摩尔有限状态机的不同之处。

对于状态机的描述方式,可分为一段式、两段式以及三段式。
一段式:整个状态机写到一个 always 模块里面。在该模块中既描述状态转移,又描述状态的输入和输出。
两段式:用两个 always 模块来描述状态机。其中一个 always 模块采用同步时序描述状态转移,另一个模块采用组合逻辑判断状态转移条件,描述状态转移规律及其输出。
三段式:在两个 always 模块描述方法基础上,使用三个 always 模块。一个 always 模块采用同步时序描述状态转移,一个 always 采用组合逻辑判断状态转移条件,描述状态转移规律,另一个 always 模块描述状态输出(可以用组合电路输出,也可以时序电路输出)。

本篇文章主要以三个简单案例为主入门状态机,需要深入学习状态机相关的知识可以参考大佬的文章:FPGA状态机(一段式、二段式、三段式)、摩尔型(Moore)和米勒型(Mealy)

三、Hello例程分析

Hello例程说明:使用状态机对“Hello”字符串进行检测,当检测到完整的“Hello”字符串时,改变led的电平。

状态转移过程说明:在状态H时,当检测到字符H,跳转到状态e,否则,一直保持在H状态;在状态e时,当检测到字符e,跳转到状态l,否则,跳转回H状态;…同理,在状态o时,当检测到字符o,跳转到状态H,同时改变led的电平,否则,跳转回H状态且不改变led电平。

为了便于转移过程的分析,使用独热码对状态进行编码:

localparam ST_H 	= 5'b00001;
localparam ST_e 	= 5'b00010;
localparam ST_la 	= 5'b00100;
localparam ST_lb 	= 5'b01000;
localparam ST_o 	= 5'b10000;

同时,需要定义一个状态机寄存器,用于判断当前的状态

//状态机寄存器
reg[4:0] curr_st;

由于对“Hello”字符串的检测分为5个状态,使用独热码进行编码占用了5个位宽,因此也需要定义一个5个位宽的状态机寄存器。

为了便于模块的维护和管理,在此定义好led的亮和灭:

//定义led状态
parameter led_on  = 1'b0;
parameter led_off = 1'b1;

根据上述状态转移的说明分析,编写状态机主程序

//状态机主程序
always@(posedge Clk or negedge Rst_n)begin
	if(!Rst_n)
		curr_st <= ST_H;
	else begin 
		case(curr_st)
			ST_H:begin
				if(data == "H")
					curr_st <= ST_e;
				else
					curr_st <= ST_H;
			end
			ST_e:begin
				if(data == "e")
					curr_st <= ST_la;
				else
					curr_st <= ST_H;
			end
			ST_la:begin
				if(data == "l")
					curr_st <= ST_lb;
				else
					curr_st <= ST_H;
			end
			ST_lb:begin
				if(data == "l")
					curr_st <= ST_o;
				else
					curr_st <= ST_H;
			end
			ST_o:begin
				if(data == "o")
					curr_st <= ST_H;
				else
					curr_st <= ST_H;
			end
			default:curr_st <= ST_H;
		endcase
	end
end

“Hello”例程的完整代码:

FSM_Hello.v:

/
//模块:状态机例程-Hello
//作者:CSDN-小夏与酒

module FSM_Hello(
	input 			Clk,
	input 			Rst_n,
	input 		 	[7:0]data,
	output 	reg 	led
);

	//状态机状态定义
	//使用独热码的编码方式
	//写法一:
	localparam ST_H 	= 5'b00001;
	localparam ST_e 	= 5'b00010;
	localparam ST_la 	= 5'b00100;
	localparam ST_lb 	= 5'b01000;
	localparam ST_o 	= 5'b10000;

	//写法二:
	//	localparam
	//			ST_H 	= 5'b00001,
	//          ST_e 	= 5'b00010,
	//          ST_la 	= 5'b00100,
	//          ST_lb 	= 5'b01000,
	//			ST_o 	= 5'b10000;
				
	//状态机寄存器
	reg[4:0] curr_st;
	
	//定义led状态
	parameter led_on  = 1'b0;
	parameter led_off = 1'b1;
	
	//状态机主程序
	always@(posedge Clk or negedge Rst_n)begin
		if(!Rst_n)
			curr_st <= ST_H;
		else begin 
			case(curr_st)
				ST_H:begin
					if(data == "H")
						curr_st <= ST_e;
					else
						curr_st <= ST_H;
				end
				ST_e:begin
					if(data == "e")
						curr_st <= ST_la;
					else
						curr_st <= ST_H;
				end
				ST_la:begin
					if(data == "l")
						curr_st <= ST_lb;
					else
						curr_st <= ST_H;
				end
				ST_lb:begin
					if(data == "l")
						curr_st <= ST_o;
					else
						curr_st <= ST_H;
				end
				ST_o:begin
					if(data == "o")
						curr_st <= ST_H;
					else
						curr_st <= ST_H;
				end
				default:curr_st <= ST_H;
			endcase
		end
	end

	//给led赋值,如果进入ST_o状态,并且data = "o",则led电平改变
	always@(posedge Clk or negedge Rst_n)begin
		if(!Rst_n)
			led <= led_off;
		else if(curr_st == ST_o && data == "o")
			led <= led_on;
		else
			led <= led_off;
	end

endmodule

RTL视图:

RTL33
状态转移333

编写测试激励文件:

FSM_Hello_tb.v:

`timescale 1ns/1ns
`define clock_period 20

module FSM_Hello_tb;

	reg Clk;
	reg Rst_n;
	reg [7:0]ASCII;
	
	wire led;
	
	FSM_Hello FSM_Hello0(
		.Clk(Clk),
		.Rst_n(Rst_n),
	 	.data(ASCII),
	 	.led(led)
	);

	initial Clk = 1;
	always #(`clock_period/2) Clk = ~Clk;
	
	initial begin
		Rst_n = 0;
		ASCII = 0;
		#(`clock_period*20);
		Rst_n = 1;
		#(`clock_period*20 + 1);
		
		ASCII = "I";
		#(`clock_period);
		ASCII = "A";
		#(`clock_period);
		ASCII = "M";
		#(`clock_period);
		ASCII = "X";
		
		#(`clock_period);
		ASCII = "H";
		#(`clock_period);
		ASCII = "E";
		#(`clock_period);
		ASCII = "M";
		#(`clock_period);
		ASCII = "l";
		#(`clock_period);

		ASCII = "H";
		#(`clock_period);
		ASCII = "E";
		#(`clock_period);
		ASCII = "L";
		#(`clock_period);
		ASCII = "L";
		#(`clock_period);
		ASCII = "O";
		#(`clock_period);
		
		ASCII = "H";
		#(`clock_period);
		ASCII = "e";
		#(`clock_period);
		ASCII = "l";
		#(`clock_period);
		ASCII = "l";
		#(`clock_period);
		ASCII = "o";
		
		#(`clock_period);
		ASCII = "l"; 
		#(`clock_period);
		
		ASCII = "H";
		#(`clock_period);
		ASCII = "e";
		#(`clock_period);
		ASCII = "l";
		#(`clock_period);
		ASCII = "l";
		#(`clock_period);
		ASCII = "o";
		
		#(`clock_period);
		ASCII = "l"; 
		
		#(`clock_period);
		
		$stop;
	end		

endmodule

仿真结果:

仿真结果

四、简单例程分析

4.1 使用状态机实现流水灯

使用Verilog实现一个流水灯并不难,但是使用状态机的方式实现,就提供了新的编程思路。

在状态机的文章中加入流水灯的实现,主要是为了学习 【小月电子】 大佬的状态机写法风格,大佬博客主页链接:Moon_3181961725

先上完整代码:

FSM_01_led.v:

/
//模块:状态机例程-LED-分析
//作者:CSDN-小夏与酒

module FSM_01_led(
	input 		Clk,
	input 		Rst_n,
	output reg 	led1,
	output reg 	led2,
	output reg 	led3
	);
	
	//定义状态机
	parameter	ST1		= 1;
	parameter	ST2		= 2;
	parameter	ST3		= 3;
	
	reg[3:0]	curr_st;//状态机寄存器
	reg[7:0]	cnt1;//定义计数寄存器
	reg[7:0]	cnt2;//定义计数寄存器
	reg[7:0]	cnt3;//定义计数寄存器
	
	//状态机主程序
	always@(posedge Clk or negedge Rst_n)begin
		if(!Rst_n)
			curr_st <= ST1;
		else case(curr_st)
			ST1:begin
				if(cnt1 == 8'd9)
					curr_st <= ST2;
				else;	
			end
			ST2:begin
				if(cnt2 == 8'd9)
					curr_st <= ST3;
				else;	
			end
			ST3:begin
				if(cnt3 == 8'd9)
					curr_st <= ST1;
				else;	
			end
			default:;
		endcase
	end
	
	//状态机ST1的计数器,当状态机等于ST1时,cnt1加1,否则cnt1等于0
	always@(posedge Clk or negedge Rst_n)begin
		if(!Rst_n)
			cnt1 <= 8'b0;
		else if(curr_st == ST1)
			cnt1 <= cnt1 + 1'b1;
		else
			cnt1 <= 8'b0;	
	end
	
	//状态机ST2的计数器,当状态机等于ST2时,cnt2加1,否则cnt2等于0
	always@(posedge Clk or negedge Rst_n)begin
		if(!Rst_n)
			cnt2 <= 8'b0;
		else if(curr_st == ST2)
			cnt2 <= cnt2 + 1'b1;
		else
			cnt2 <= 8'b0;	
	end
	
	//状态机ST3的计数器,当状态机等于ST3时,cnt3加1,否则cnt3等于0
	always@(posedge Clk or negedge Rst_n)begin
		if(!Rst_n)
			cnt3 <= 8'b0;
		else if(curr_st == ST3)
			cnt3 <= cnt3 + 1'b1;
		else
			cnt3 <= 8'b0;	
	end
	
	//给LED1赋值,当状态等于ST1时,LED1等于0,即点亮LED灯,否则LED1等于1,关闭LED灯
	always@(posedge Clk or negedge Rst_n)begin
		if(!Rst_n)
			led1 <= 1'b1;
		else if(curr_st == ST1)
			led1 <= 1'b0;
		else
			led1 <= 1'b1;	
	end
	
	//给LED2赋值,当状态等于ST2时,LED2等于0,即点亮LED灯,否则LED2等于1,关闭LED灯
	always@(posedge Clk or negedge Rst_n)begin
		if(!Rst_n)
			led2 <= 1'b1;
		else if(curr_st == ST2)
			led2 <= 1'b0;
		else
			led2 <= 1'b1;	
	end
	
	//给LED3赋值,当状态等于ST3时,LED3等于0,即点亮LED灯,否则LED3等于1,关闭LED灯
	always@(posedge Clk or negedge Rst_n)begin
		if(!Rst_n)
			led3 <= 1'b1;
		else if(curr_st == ST3)
			led3 <= 1'b0;
		else
			led3 <= 1'b1;	
	end
	
endmodule
	

RTL视图与状态转移:

RTL1
状态转移

FSM_01_led_tb.v:

`timescale 1ns/1ns
`define clock_period 20

module FSM_01_led_tb;
	
	reg Clk;
	reg Rst_n;
	
	wire led1;
	wire led2;
	wire led3;
	
	FSM_01_led FSM_01_led0(
		.Clk(Clk),
		.Rst_n(Rst_n),
		.led1(led1),
		.led2(led2),
		.led3(led3)
	);
	
	initial Clk = 1;
	always #(`clock_period/2) Clk = ~Clk;
	
	initial begin
	Rst_n = 0;
	
	#(`clock_period*30);
	Rst_n = 1;
	
	#(`clock_period*300);
	
	$stop;
	
	end
	
endmodule

仿真结果:

仿真结果

实现效果:

在上板验证前记得修改计数器的计数值,即:

/
//模块:状态机例程-LED
//作者:CSDN-小夏与酒

module FSM_01_led(
	input 		Clk,
	input 		Rst_n,
	output reg 	led1,
	output reg 	led2,
	output reg 	led3
	);
	
	//定义状态机
	parameter	ST1		= 1;
	parameter	ST2		= 2;
	parameter	ST3		= 3;
	
	reg[3:0]	curr_st;//状态机寄存器
	reg[24:0]	cnt1;//定义计数寄存器
	reg[24:0]	cnt2;//定义计数寄存器
	reg[24:0]	cnt3;//定义计数寄存器
	
	//定义计数值范围
	parameter cnt_max = 25'd24_999_999;	//定时器最大值
	parameter cnt_min = 25'd0;			//定时器最小值
	parameter cnt_add = 1'b1;			//定时器定时增量
	
	//定义led状态
	parameter led_on  = 1'b0;
	parameter led_off = 1'b1;
	
	//状态机主程序
	always@(posedge Clk or negedge Rst_n)begin
		if(!Rst_n)
			curr_st <= ST1;
		else case(curr_st)
			ST1:begin
				if(cnt1 == cnt_max)
					curr_st <= ST2;
				else;	
			end
			ST2:begin
				if(cnt2 == cnt_max)
					curr_st <= ST3;
				else;	
			end
			ST3:begin
				if(cnt3 == cnt_max)
					curr_st <= ST1;
				else;	
			end
			default:;
		endcase
	end
	
	//状态机ST1的计数器,当状态机等于ST1时,cnt1加1,否则cnt1等于0
	always@(posedge Clk or negedge Rst_n)begin
		if(!Rst_n)
			cnt1 <= cnt_min;
		else if(curr_st == ST1)
			cnt1 <= cnt1 + cnt_add;
		else
			cnt1 <= cnt_min;	
	end
	
	//状态机ST2的计数器,当状态机等于ST2时,cnt2加1,否则cnt2等于0
	always@(posedge Clk or negedge Rst_n)begin
		if(!Rst_n)
			cnt2 <= cnt_min;
		else if(curr_st == ST2)
			cnt2 <= cnt2 + cnt_add;
		else
			cnt2 <= cnt_min;	
	end
	
	//状态机ST3的计数器,当状态机等于ST3时,cnt3加1,否则cnt3等于0
	always@(posedge Clk or negedge Rst_n)begin
		if(!Rst_n)
			cnt3 <= cnt_min;
		else if(curr_st == ST3)
			cnt3 <= cnt3 + cnt_add;
		else
			cnt3 <= cnt_min;	
	end
	
	//给LED1赋值,当状态等于ST1时,LED1等于0,即点亮LED灯,否则LED1等于1,关闭LED灯
	always@(posedge Clk or negedge Rst_n)begin
		if(!Rst_n)
			led1 <= led_off;
		else if(curr_st == ST1)
			led1 <= led_on;
		else
			led1 <= led_off;	
	end
	
	//给LED2赋值,当状态等于ST2时,LED2等于0,即点亮LED灯,否则LED2等于1,关闭LED灯
	always@(posedge Clk or negedge Rst_n)begin
		if(!Rst_n)
			led2 <= led_off;
		else if(curr_st == ST2)
			led2 <= led_on;
		else
			led2 <= led_off;	
	end
	
	//给LED3赋值,当状态等于ST3时,LED3等于0,即点亮LED灯,否则LED3等于1,关闭LED灯
	always@(posedge Clk or negedge Rst_n)begin
		if(!Rst_n)
			led3 <= led_off;
		else if(curr_st == ST3)
			led3 <= led_on;
		else
			led3 <= led_off;	
	end
	
endmodule
	

实现效果

学习方法:

将状态机主程序分割开来单独写,会使整个程序思路清晰。状态机主程序部分:

//状态机主程序
always@(posedge Clk or negedge Rst_n)begin
	if(!Rst_n)
		curr_st <= ST1;
	else case(curr_st)
		ST1:begin
			if(cnt1 == 8'd9)
				curr_st <= ST2;
			else;	
		end
		ST2:begin
			if(cnt2 == 8'd9)
				curr_st <= ST3;
			else;	
		end
		ST3:begin
			if(cnt3 == 8'd9)
				curr_st <= ST1;
			else;	
		end
		default:;
	endcase
end

4.2 使用状态机实现循迹小车的pwm

关于FPGA实现简单的循迹小车链接:【FPGA-Spirit_V2】基于FPGA的循迹小车-小精灵V2开发板

现在讲解小车中的pwm模块

关于模块的端口列表:

module ctrl_moto_pwm(
	input				clk,//时钟50M
	input				rst_n,//复位,低电平有效
	input	[7:0]		spd_high_time,//输入高电平持续时间
	input	[7:0]		spd_low_time,//输入低电平持续时间
	output				period_fini,//一个pwm周期结束的标志位
	output	reg			pwm//脉冲信号									
);

该模块需要输入时钟信号、复位信号、高电平持续时间和低电平持续时间;输出一个pwm周期结束的标志位和pwm信号

定义pwm产生的三个状态

//状态机
parameter	idle		= 8'h0;//空闲状态
parameter	step_high 	= 8'h1;//脉冲高电平状态,当为该状态时,pwm为高电平
parameter	step_low  	= 8'h2;//脉冲低电平状态,当为该状态时,pwm为低电平

为了debug的方便,在模块中加入判断产生了一个pwm的标志位

//产生一个pwm周期的标志位,当一个pwm产生后,输出高电平,否则输出低电平
assign period_fini = (step_low_cnt == step_low_time)?1'b1:1'b0;

同样,与上文相同,为了编程思路的清晰,将状态机主程序单独写

//状态机主程序
always@(posedge clk or negedge rst_n)begin
	if(!rst_n)
		curr_st <= idle;
	else case(curr_st)
		idle:curr_st <= step_high;
		step_high:begin
				//当高电平计数时间到达输入值时,进行状态跳转
				if(step_high_cnt == step_high_time)
					curr_st <= step_low;
				else;
		end
		step_low:begin
				//当低电平计数时间到达输入值时,进行状态跳转
				if(step_low_cnt == step_low_time)
					curr_st <= step_high;
				else;
		end
		default:;
	endcase
end

在不同的状态下(空闲、高电平、低电平),pwm的信号输出:

//该always块描述pwm的输出
always@(posedge clk or negedge rst_n)begin
	if(!rst_n)
		pwm <= 0;
	else if(curr_st == idle)
		pwm <= 0;
	else if(curr_st == step_high)
		pwm <= 1;
	else if(curr_st == step_low)
		pwm <= 0;
	else
		pwm <= 1;
end

完整代码如下:

ctrl_moto_pwm.v:

//脉冲生成模块,通过控制输出脉冲频率及占空比来控制小车的速度
module ctrl_moto_pwm(
	input				clk,//时钟50M
	input				rst_n,//复位,低电平有效
	input	[7:0]		spd_high_time,//输入高电平持续时间
	input	[7:0]		spd_low_time,//输入低电平持续时间
	output				period_fini,//一个pwm周期结束的标志位
	output	reg			pwm//脉冲信号									
);
				
	//状态机
	parameter	idle		= 8'h0;//空闲状态
	parameter	step_high 	= 8'h1;//脉冲高电平状态,当为该状态时,pwm为高电平
	parameter	step_low  	= 8'h2;//脉冲低电平状态,当为该状态时,pwm为低电平

	reg	[7:0]	curr_st;
	reg	[10:0]	step_high_time;
	reg	[10:0]	step_low_time;
	reg	[10:0]	step_high_cnt;
	reg	[10:0]	step_low_cnt;
	
	//产生一个pwm周期的标志位,当一个pwm产生后,输出高电平,否则输出低电平
	assign period_fini = (step_low_cnt == step_low_time)?1'b1:1'b0;
	
	//将输入值(高、低电平持续时间)存入寄存器中
	always@(posedge clk or negedge rst_n)begin
		if(!rst_n)begin
				step_high_time <= 0;
				step_low_time <= 0;
		end
		else begin
				step_high_time <= spd_high_time;
				step_low_time <= spd_low_time;
		end
	end
	
	//状态机主程序
	always@(posedge clk or negedge rst_n)begin
		if(!rst_n)
			curr_st <= idle;
		else case(curr_st)
			idle:curr_st <= step_high;
			step_high:begin
					//当高电平计数时间到达输入值时,进行状态跳转
					if(step_high_cnt == step_high_time)
						curr_st <= step_low;
					else;
			end
			step_low:begin
					//当低电平计数时间到达输入值时,进行状态跳转
					if(step_low_cnt == step_low_time)
						curr_st <= step_high;
					else;
			end
			default:;
		endcase
	end
	
	//高电平持续时间计数器,当持续时间到达输入值时,进行状态跳转
	always@(posedge clk or negedge rst_n)begin
		if(!rst_n)
			step_high_cnt <= 0;
		else if(curr_st == idle)
			step_high_cnt <= 0;
		else if(curr_st == step_high)
			step_high_cnt <= step_high_cnt + 1;
		else
			step_high_cnt <= 0;
	end
	
	//低电平持续时间计数器,当持续时间到达输入值时,进行状态跳转
	always@(posedge clk or negedge rst_n)begin
		if(!rst_n)
			step_low_cnt <= 0;
		else if(curr_st == idle)
			step_low_cnt <= 0;
		else if(curr_st == step_low)
			step_low_cnt <= step_low_cnt + 1;
		else
			step_low_cnt <= 0;
	end
	
	//该always块描述pwm的输出
	always@(posedge clk or negedge rst_n)begin
		if(!rst_n)
			pwm <= 0;
		else if(curr_st == idle)
			pwm <= 0;
		else if(curr_st == step_high)
			pwm <= 1;
		else if(curr_st == step_low)
			pwm <= 0;
		else
			pwm <= 1;
	end

endmodule

pwm产生模块的代码中我每一部分都写了对应的注释,可供大家参考,该模块可以直接在需要用到pwm的地方直接调用

该模块对应的RTL视图和状态转移:
RTL4
状态转移4
我们编写一个简单的测试激励文件:

ctrl_moto_pwm_tb.v:

`timescale 1ns/1ns
`define clock_period 20

module ctrl_moto_pwm_tb;
	
	reg clk;
	reg rst_n;
	reg [7:0]spd_high_time;
	reg [7:0]spd_low_time;
	
	wire period_fini;
	wire pwm;
	
	ctrl_moto_pwm Uctrl_moto_pwm0(
		.clk(clk),//时钟50M
		.rst_n(rst_n),//复位,低电平有效
		.spd_high_time(spd_high_time),//输入高电平持续时间
		.spd_low_time(spd_low_time),//输入低电平持续时间
		.period_fini(period_fini),//一个pwm周期结束的标志位
		.pwm(pwm)//脉冲信号									
	);

	initial clk = 1;
	always #(`clock_period/2) clk = ~clk;
	
	initial begin
	
		rst_n = 0;
		spd_high_time = 0;
		spd_low_time = 0;
		#(`clock_period*100);
	
		rst_n = 1;
		spd_high_time = 15;
		spd_low_time = 5;
		#(`clock_period*2000);
		
		$stop;
	
	end

endmodule

仿真结果:

仿真4

csdn

🧸结尾


  • 17
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 5
    评论
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

小夏与酒

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

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

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

打赏作者

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

抵扣说明:

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

余额充值