单片机uart实现modbus-rtu协议

双11提前购!买周边/年卡,一键领4重福利! 送200元神券包+10%红包返利+最高10年VIP!还能抽iPhone18Pro!仅限1次,速抢>> 阅读详情

modbus

uart波特率为9600,modbus帧间隔3.5个字符即3.5ms,开启一个4ms的定时器,在uart接收中断中刷新4ms定时器,定时器超时则表示连续4ms没有接收到数据,认为一帧结束,mb_frame_end_flag = 1;

  • modbus.h
/* 寄存器映射表 */
struct reg_map_table {
	u16 start_addr;
	u16 count;
	u16 *p_reg_struct;
};

typedef struct {
    u16 cell_vol1;
    u16 cell_vol2;
    u16 cell_vol3;
    u16 cell_vol4;
    s16 current;
    s16 temp1;
    s16 temp2;
    ......
}bms_info_st;

typedef struct {
    u16 over_vol;
    s16 over_cur;
    s16 over_temp;
    ......
}protect_para_st;

typedef struct {
	u16 hw_ver[10];
	u16 sw_ver[10];
    u16 sys_sn[10];
    ......
}sys_para_st;

typedef struct {
    u16 vol_gain;
    s16 cur_offset;
    s16 cur_gain;
    u16 temp_offset;
    ......
}cali_para_st;

/* 4组寄存器 */
extern bms_info_st g_bms_info;  //采集的数据,只读
extern protect_para_st g_protect_para;  //设置的保护参数,可读可写入flash
extern sys_para_st g_sys_para;
extern cali_para_st g_cali_para;

extern u8 mb_buf[256];
extern u16 mb_cnt;
extern u8 mb_frame_end_flag;

extern void modbus_task(void);
  • modbus.c
#include "modbus.h"

const u8 mb_salve_addr = 0x01;  //modbus从机地址
u16* p_reg;  //寄存器映射指针

u8 mb_buf[256] = {0};  //uart接收缓冲
u16 mb_cnt = 0;  //uart接收一帧的长度
u8 mb_frame_end_flag = 0;  //帧结束标志

bms_info_st g_bms_info;  //运行数据
protect_para_st g_protect_para;  //保护参数
sys_para_st g_sys_para;  //系统参数
cali_para_st g_cali_para;  //校准参数

struct reg_map_table mb_reg_map_table[4] = {
	{  0, sizeof(g_bms_info)/2, &g_bms_info},
	{256, sizeof(g_protect_para)/2, &g_protect_para},
	{512, sizeof(g_sys_para)/2, &g_sys_para},
	{768, sizeof(g_sys_para)/2, &g_cali_para},
};

/* modbus CRC16 */
u16 mb_crc16(u8 *buf, u16 len)
{
    u16 crc = 0xffff;
    u16 i = 0, j = 0;
    
    for (i = 0; i < len; i++)
    {
        crc ^= buf[i];
        
        for (j = 0; j < 8; j++)
        {
            if (crc & 0x01)
            {
                crc >>= 1;
                crc ^= 0xA001;
            }
            else
            {
                crc >>= 1;
            }				
        }
    }   
    return (crc);
}

/* 地址映射寄存器(结构体), 返回错误码 */
u8 mb_reg_map(u16 addr, u16 count)
{
	u8 i = 0;

	for (i = 0; i < 4; i++)
	{
		if ((addr >= mb_reg_map_table[i].start_addr)
			&& (addr <= mb_reg_map_table[i].start_addr + mb_reg_map_table[i].count))
		{
			if ((addr + count) <= (mb_reg_map_table[i].start_addr + mb_reg_map_table[i].count))
			{
				p_reg = mb_reg_map_table[i].p_reg_struct + addr - mb_reg_map_table[i].start_addr;
				return 0;
			}
			else
			{
				return 0x03;  //请求的数据长度错误
			}
		}
	}
	return 0x02;  //起始地址错误
}

/* 错误响应帧:
 * 1byte 从机地址
 * 1byte 功能码 + 0x80
 * 1byte 错误码: 
 * 		0x01->不支持的功能码
 * 		0x02->起始地址错误
 * 		0x03->请求的数据长度错误
 * 		0x04->数据读写失败
 * 2bytes CRC16
 */
void mb_err_ack(u8 op_code, u8 err_code)
{
	u8 mb_txbuf[5];
	u16 crc16;
	mb_txbuf[0] = mb_salve_addr;
	mb_txbuf[1] = op_code | 0x80;
	mb_txbuf[2] = err_code;
	crc16 = mb_crc16(mb_txbuf, 3);
	mb_txbuf[3] = crc16 >> 8;
	mb_txbuf[4] = crc16;
	uart2_tx(mb_txbuf, 5);
}

//0x03: 读寄存器指令
/* 1byte 从机地址
 * 1byte 功能码:0x03
 * 2bytes 起始寄存器地址
 * 2bytes 寄存器个数N
 * 2bytes CRC16
 */
//响应: 地址 + 0x03 + 字节数(1B) + N*寄存器值 + CRC16
void mb_read_reg(u16 reg_addr, u16 count)
{
	u16 crc16;
	u8 i = 0;
	u8 err_code = 0;

	/* 映射寄存器 */
	err_code = mb_reg_map(reg_addr, count);
	if (err_code)
	{
		mb_err_ack(0x03, err_code);
		return;
	}
	
	mb_buf[0] = mb_salve_addr;
	mb_buf[1] = 0x03;
	mb_buf[2] = 2*count;
	for (i = 0; i < count; i++)
	{
		mb_buf[2*i + 3] = p_reg[i] >> 8;
		mb_buf[2*i + 4] = p_reg[i];
	}
	crc16 = mb_crc16(mb_buf, 2*count + 3);
	mb_buf[3 + 2*count] = crc16 >> 8;
	mb_buf[4 + 2*count] = crc16;
	uart2_tx(mb_buf, 5 + 2*count);
}

//0x06: 写单个寄存器
/* 1byte 从机地址
 * 1byte 功能码:0x06
 * 2bytes 寄存器地址
 * 2bytes 寄存器值
 * 2bytes CRC16
 */
//响应: 同接收
void mb_write_reg(u16 addr, u16 val)
{
	u8 i = 0;
	u16 VPackTemp = 0;
	s16 cur_temp = 0;
	
	/* 映射寄存器 */
	u8 err_code = mb_reg_map(addr, 0);
	if (err_code)
	{
		mb_err_ack(0x06, err_code);
		return;
	}
	
	*p_reg = val;
	
	switch (addr)
	{
		case 0x0300:
			g_bms_info.pack_v = val >> 8 | val << 8;
			for(i=ucCellNumOffset; i<(ucCellNumOffset+ucCellNum); i++)
			{
				VPackTemp += AFE.uiCell[i];
			}
			E2uiVPackGain = (U32)CALIPACKVOL*VPackTemp / val;
		break;
		case 0x0301:
			E2siCadcOffset = AFE.siCurr;
		break;
		case 0x0302:
			cur_temp = val >> 8 | val << 8;
			E2siCadcGain = (S32)CALICUR * (AFE.siCurr - E2siCadcOffset) / cur_temp;
		break;
	}
	
	uart2_tx(mb_buf, 8);
}

//0x10: 写多个寄存器
/* 1byte 从机地址
 * 1byte 功能码:0x10
 * 2bytes 寄存器起始地址
 * 2bytes 寄存器个数 N
 * 1byte 字节数 2*N
 * 2*N bytes 2*N 要写入寄存器的值
 * 2bytes CRC16
 */
//响应: 地址 + 0x10 + 寄存器起始地址 + 寄存器个数N + CRC16
void mb_write_multi_reg(u16 addr, u16 count, u16* val)
{
	u16 crc16;
	u8 i = 0;
	/* 映射寄存器 */
	u8 err_code = mb_reg_map(addr, count);
	if (err_code)
	{
		mb_err_ack(0x10, err_code);
		return;
	}
	
	my_memcpy((u8*)val, (u8*)p_reg, 2 * count);

	mb_buf[0] = mb_salve_addr;
	mb_buf[1] = 0x10;
	mb_buf[2] = addr >> 8;
	mb_buf[3] = addr;
	mb_buf[4] = count >> 8;
	mb_buf[5] = count;
	crc16 = mb_crc16(mb_buf, 6);
	mb_buf[6] = crc16 >> 8;
	mb_buf[7] = crc16;
	uart2_tx(mb_buf, 8);
}

/* crc16校验 */
static u8 mb_crc16_check(void)
{
	u16 recv_crc16 = 0;
	u16 calc_crc16 = 0;
	
	recv_crc16 = mb_buf[mb_cnt - 1] | mb_buf[mb_cnt - 2] << 8;
	calc_crc16 = mb_crc16(mb_buf, mb_cnt - 2);
	
	return (calc_crc16 == recv_crc16);
}

/* 上位机读取数据前,更新modbus协议的数据 */
static void mb_get_data(void)
{
    //禁止uart接收中断
    my_memset(0);
    
    g_bms_info.cell_vol1 = 采集的电压1;
    g_bms_info.cell_vol2 = 采集的电压2;    
    ......
    ......    
    //使能uart接收中断
}

/* 上位机下发设置参数后,写入flash */
static void mb_set_data(void)
{
    //禁止uart接收中断
	E2uiOVvol			= g_protect_para.over_vol;
	E2uiOCcur			= g_protect_para.over_cur;
    ......
    ......
    write_flash_flag = 1;
    //使能uart接收中断
}

/* 解析帧数据 */
void mb_parse_cmd(void)
{
	u8 slave_addr = 0;
	u8 op_code = 0;
	u16 reg_addr = 0;
	u16 reg_cnt = 0;
	u16 reg_val = 0;
	u16* p_val = NULL;
	
	slave_addr = mb_buf[0];
	reg_addr = mb_buf[2] << 8 | mb_buf[3];
	reg_cnt = mb_buf[4] << 8 | mb_buf[5];
	reg_val = mb_buf[4] << 8 | mb_buf[5];
	p_val = (u16*)&mb_buf[7];
	
	if (slave_addr == mb_salve_addr)
	{
		op_code = mb_buf[1];
		
		switch (op_code) 
		{
			case 0x03:
				mb_get_data();
				mb_read_reg(reg_addr, reg_cnt);
			break;
			case 0x06:
				mb_get_data();
				mb_write_reg(reg_addr, reg_val);
				mb_set_data();
			break;
			case 0x10:
				mb_get_data();
				mb_write_multi_reg(reg_addr, reg_cnt, p_val);
				mb_set_data();
			break;
			default:
				mb_err_ack(op_code, 0x01);  /* 不支持的op_code */
			break;
		}
		
	}
	else
	{
		return;
	}
}

void modbus_task(void)
{
	if (mb_frame_end_flag == 1)
	{
		mb_frame_end_flag = 0;
		
		if (mb_crc16_check())
		{	
			mb_parse_cmd();  /* 解析协议 */
		}
		else
		{
			goto frame_err;
		}

frame_err:
	//	memset(mb_buf, mb_cnt, 0);
		mb_cnt = 0;
	}
}
  • main.c
while(1)
{
    modbus_task();
    if (write_flash_flag)
    {
        write_flash_flag = 0;
        //TODO: 写flash
    }
}
买完就返现!双11神券包+10%余额红包大派送! 买周边/年卡再抽iPhone18Pro,最高赢10年VIP!手慢无,戳我领取>> 阅读详情

相关推荐

C# ModbusRtu 通讯例程详解

工控,通讯则是基础。今天我们就来一起学习一下关于ModbusRtu通讯的相关内容。适合初学者积累相关经验。本文含有ModbusRtu通讯库,通讯示例。 硬件设备包括电推杆、压力变送器等。ModbusRtu通讯库内含方法包括读取输入线圈、读取输出线圈、读取输出寄存器、发送指令、CRC校验等。

weixin_50236846的博客 4223

PLC程序实例三:ModBusRTU客户端编程实例与测试方法

PLC程序实例分享,即根据项目情况分享不同实例。

weixin_43431593的博客 2939

stm32基于UART串口实现modbusRTU(软件方式)

stm32简单实现modbusRTU协议

qq_50532565的博客 4556

一个ModBus RTU程序(支持01、02、03、05、06、15、16功能码)

有两点需要注意的:1、modbus rtu标准协议规定:每帧数据间隔至少为3.5个字符时间。 2、接收发送数据最好改为中断模式,不然通信速度不够。 #include "RS485.h" #include "CRC.h" /////////////////////////////////////////////////////////// u32 RS485_Baudrate=9600;//通

僧哥叨叨叨 4万+

STC15单片机-上位机通过Modbus-RTU协议与开发板通信

Modbus是一种串行通信协议,是Modicon公司(现在的施耐德电气Schneider Electric)于 1979年为使用可编程逻辑控制器(PLC)通信而发表。Modbus 已经成为工业领域通信协议的业界标准(De facto),并且现在是工业电子设备之间常用的连接方式。更详细的介绍可看这篇文章:http://t.csdn.cn/n1T5O要进行Modbus通信,就要先写一个上位机软件,是用C#语言编写的(还不会,先用现成的);然后电脑通过USB转485工具与开发板的A、B端子相连接,Modbus协议

weixin_46251230的博客 4705

LabVIEW与单片机串口通信实战:从UART到Modbus RTU的完整指南

串口通信是嵌入式系统与上位机交互的基础技术,其核心在于通信双方对数据传输格式的约定一致。UART作为最常用的物理层接口,通过波特率、数据位、校验位等参数定义字节传输规则,而VISA则提供了跨平台的统一编程接口。在LabVIEW环境中,正确配置串口参数、处理字节数组与字符串的转换,是保证数据可靠收发的前提。针对实际工程中的乱码、丢帧、超时等问题,系统性的排查方法至关重要。进一步地,RS485与Modbus RTU协议扩展了串口通信的工业应用场景,UDP则适用于远程监控需求。掌握这些技术,能让开发者高效构建La

weixin_29175469的博客 316

Modbus RTU(Remote Terminal Unit)协议在单片机上的实现(RS485、UART)

ModbusRTU协议

qq_41918130的博客 2069

89C52单片机Modbus通信协议源码实战项目

89C52采用高性能CMOS 8位微控制器内核,基于经典的Harvard架构,内置4KB可重复编程Flash程序存储器和256字节RAM数据存储器,支持最高12MHz工作频率。其核心组件包括8位CPU、专用寄存器组(如PSW、DPTR)、5个中断源、3个定时器/计数器(T0/T1/T2),以及4组双向8位I/O端口(P0-P3)。

weixin_35516624的博客 1043

波特率115200,51单片机Modbus RTU

本文介绍了一个基于51单片机的Modbus RTU串口通信实现方案。系统采用115200波特率,通过串口与电脑连接,支持Modbus协议功能码03(读保持寄存器)和06(写单个寄存器)。代码实现了串口初始化、CRC校验计算、寄存器读写操作以及异常处理等功能。主程序循环处理Modbus请求并显示接收数据,中断服务程序负责数据接收。系统测试时需通过串口调试助手发送指令,验证通信功能。该方案适用于工业控制领域的数据采集和设备控制应用。

cxhust001688的博客 103

STC15单片机实现Modbus RTU通信实战指南

Modbus RTU是一种广泛应用于工业现场的串行通信协议,其核心在于严格的帧格式、CRC-16校验与字符间隔定时。在8051架构单片机中,STC15系列因采用XFR寄存器机制、可配置UART0中断向量及T1/T2协同定时等硬件特性,无法直接移植标准C51 Modbus代码,必须重写底层驱动。技术价值体现在高可靠性帧接收(3.5字符空闲检测)、Flash驻留CRC查表加速、以及寄存器地址到物理内存的显式映射。典型应用场景包括PLC从站、智能电表、RS-485工业IO模块等嵌入式终端设备。本文聚焦STC15W

weixin_28366475的博客 219

基于N76E003与CS1237的Modbus RTU工业数据采集节点实战

在工业物联网与嵌入式测控领域,高精度数据采集是系统可靠性的基石。24位ADC作为模拟前端核心,负责将传感器微弱信号转换为数字量,而Modbus RTU凭借其帧结构紧凑、CRC校验可靠的特点,成为工业现场设备通信的主流协议之一。8位单片机如N76E003,以极低成本与低功耗特性,在简单采集节点中依然扮演重要角色。理解ADC时序驱动、协议状态机设计与资源受限下的软件裁剪,有助于工程师快速构建稳定的测量系统。这类方案广泛适用于工业称重、压力变送、温度采集等低功耗小节点场景。文章以新唐N76E003作为主控,结合国

weixin_29165725的博客 79

HRTOS 实战:基于 8051 的 Modbus RTU 从站模块设计与实现

HRTOS ModbusRTU模块为8051单片机提供了高效的Modbus从站解决方案。该模块基于RTOS架构设计,采用任务调度和中断驱动机制,包含完整的协议处理、帧管理、CRC校验和寄存器管理功能。支持常用功能码,资源占用低(RAM约150字节,ROM约3KB),采用分层设计便于移植。模块包含三个核心任务:UART中断任务(优先级9)、Modbus处理任务(优先级3)和超时检测任务(优先级2),通过事件同步实现高效通信。相比裸机方案,具有结构清晰、响应快速、资源占用低等优势,适用于工业控制、智能仪表等嵌入

qq_36826123的博客 331

Modbus+RTU协议单片机例程

51单片机上运行的modbus协议的实现样例,单片机演示卡原理图.pdf,WQ_C51_3 modbus.c。

动手写写Modbus-RTU协议

关注、星标嵌入式客栈,精彩及时送达[导读] 大家好,我是逸珺。前面聊了modbus的物理层,协议标准,今天来实现一下modbus-RTU,本文主要聊从设备的实现。思路分析 前面聊modbu...

嵌入式客栈 2450

基于STM32的最简易蓝牙遥控小车

基于STM32的蓝牙遥控小车,基于HAL库编写代码,具有移动功能,读者可自行添加其他扩展功能。按下状态的对应关系:2前进,4左转,5停止,6右转,8后退,松开状态都是5,都选择16进制。首先按照小车的图纸把小车的机械部分装好。做这个小车的成本在200元左右,包括焊接设备,杜邦线等。

weixin_43469377的博客 1万+

QT使用openssl实现RSA加解密

使用openssl生成公私钥,随后用QT对相应的信息进行RSA加解密

weixin_42098322的博客 3862

STM32单片机RS485 Modbus通讯协议实现

STM32单片机RS485 Modbus通讯协议实现

cici15874的博客 563

C++ 核电站

问题描述 一个核电站有n个放核物质的坑,排列在一条直线上。如果连续m个坑中放入核物质就会发生爆炸,所以某些坑中不能放核物质。 本题的任务:对于给定的n和m,求不发生爆炸的放置核物质的方案总数。 输入格式 第一行两个正整数n和m,之间用一个空格隔开。2≤n≤50,2≤m≤5。 输出格式 一行一个正整数,表示方案总数。 输入样例 4 3 输出样例 13 数据规模与约定 2≤n≤50,2≤m≤5 CODE #include<bits/stdc++.h> using namespace

HybridCOW_HORSE的博客 1797

不止于打印:用CH552的UART1打造一个稳定可靠的数据收发模块(附中断+DMA思路)

本文详细介绍了如何利用CH552的UART1构建高可靠数据收发模块,结合中断和DMA技术实现稳定通信。通过环形缓冲区、定时器协议解析和软件DMA优化,提升数据传输效率与可靠性,适用于物联网和工控设备。

weixin_30394981的博客 377

Hadoop生态系统-新手快速入门(含HDFS、HBase系统架构)

Hadoop简介 Hadoop是一个由Apache基金会所开发的分布式系统基础架构。 用户可以在不了解分布式底层细节的情况下,开发分布式程序。充分利用集群的威力进行高速运算和存储。 Hadoop实现了一个分布式文件系统(Hadoop Distributed File System),简称HDFS。HDFS有高容错性的特点,并且设计用来部署在低廉的(low-cost)硬件上;而且它提供高吞吐量...

Hadoop环境搭建教程 465

跨越移植陷阱:ESP-IDF下LVGL与ILI9341的组件化集成与常见避坑指南

本文详细解析了在ESP-IDF环境下将LVGL图形库与ILI9341显示屏驱动进行组件化集成的完整流程。重点介绍了移植过程中的关键配置策略、内存管理优化技巧以及常见问题解决方案,帮助开发者有效规避内存溢出、显示异常等典型陷阱,实现高效稳定的嵌入式GUI开发。

yhn456789的博客 617

基于STC89C52的Modbus RTU从机完整工程实现

本工程基于STC89C52单片机,实现Modbus RTU从机功能,支持01(读线圈)、02(读离散输入)、03(读保持寄存器)、04(读输入寄存器)、05(写单个线圈)、06(写单个寄存器)等常用功能码。工程采用Keil C51开发,包含串口通信、Modbus协议解析、CRC校验、定时器超时处理等核心模块,可直接用于工业自动化、智能设备等场景的Modbus从机开发

guygg88的博客 365

PowerDesigner16.5下载和安装教程

一、软件下载 1.下载地址(不用复制,直接点击下载链接即可):链接:https://pan.baidu.com/s/1MSnWHWt3SO2Uz5dt9AANAg提取码:z9kk 二、软件安装 1.下载安装包(包含安装文件、汉化包、注册文件) 2.下载后文件内容如下 3....

cicong2374的博客 10万+

MODBUS RTU time_out实现

如 带有FIFO功能的芯片,为了保证在收到FIFO内容且没有达到FIFO中断的长度,设置了time_out中断。需要注意的是,在FIFO中断时,不要把FIFO内容完全读完,保留1个字节在FIFO中,否则可能不会发生time_out中断。这时需要设置一个根据波特率的超时时间的表格和一个time_out的变量,每收到一个字符,time_out= 超时时间;而在定时中断里面 time_out--,如果 time_out减至0,则MODBUS RTU协议的帧结束。1 uart芯片本身就提供 time_out功能。

Steelenwang的博客 589

微信小程序 - {errCode: 10008, errno: 1500104, errMsg: “writeBLECharacteristicValue:fail write characteris

微信小程序errCode: 10008, errno: 1500104, errMsg: "writeBLECharacteristicValue:fail write characteristics error. 10008, errMsg=The value's length is invalid.",uni-app微信小程序报错,uniApp小程序调用蓝牙api报错,向低功耗蓝牙设备的某个特征写入数据时错误,UniApp微信小程序蓝牙API调用报错10008解决方案,蓝牙设备进行通信时,向设备写入数据

🏆 前端领域优质创作者 4424

《QGIS快速入门与应用基础》009:国内镜像源(清华大学/阿里云)下载技巧

针对国内用户下载QGIS速度慢、易中断的问题,推荐使用清华大学和阿里云两大国内镜像源,提供3-10MB/s的极速下载方案。详细解析镜像源核心优势、分步骤下载指南及校验方法,对比清华(同步快、北方优选)与阿里云(南方节点多)的特点。针对常见问题如版本延迟、404错误等提供解决方案,并介绍搭配IDM等工具实现多线程加速的进阶技巧。通过镜像源+下载工具组合,用户可高效获取QGIS安装包,为后续系统安装奠定基础。

LuckyHanMo的博客 1709

留个记录-STM32F103 modbus RTU

STM32CUBUMX配置RS485 modbus STM32(从机)亲测可用_stm32h7 cubemx freemodbus-CSDN博客STM32CUBUMX配置RS485(中断接收)--保姆级教程_stm32cubemx rs485-CSDN博客

u010297049的博客 400

jre9下载 jdk9

(Java SE Runtime Environment 9.0) 9.0u175 官方最新版 【x86|x64】

给单片机新手的保姆级教程:用STM32CubeMX快速配置I2C、SPI和UART(对接Modbus)

本文为单片机新手提供STM32CubeMX配置I2C、SPI和UART通信协议的保姆级教程,涵盖从开发环境准备到Modbus-RTU实现的详细步骤。通过实战案例和调试技巧,帮助开发者快速掌握嵌入式总线协议配置,提升开发效率。

weixin_30603633的博客 288

pscad IEEE标准系统搭建说明

pscad IEEE标准系统搭建说明,包含典型的IEEE 9节点、14节点、30节点、39节点和118节点系统。内含标准测试系统的原始数据和潮流结果。

上一篇: C语言位域应用--一个字节存八个标志位
下一篇: GD32模拟IIC读写DVC1124
RYYB
博客等级 码龄7年 9粉丝 4原创
评论 2
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值