S3C2440 平台的spi ad7888驱动程序

2440开发板远程控制与ADC驱动集成包 S3C2440是由韩国三星公司设计的一款基于ARM920T核心的32位RISC处理器。它采用0.13微米工艺,工作频率可达400MHz至533MHz。其独特的哈佛结构拥有独立的数据总线和指令总线,支持动态电源管理以及MMU(内存管理单元)功能。S3C2440支持多种外设接口,为嵌入式系统的开发提供了便利。远程控制是一种通过网络或其他通信手段,从一处位置对另一处位置的计算机、设备或系统进行操作和管理的技术。远程控制能够在不同地理位置之间实现信息的交互和资源的共享,从而大大提高了工作效率和灵活性。 阅读详情

 

首先定义一个struct ad7888 {
struct cdev cdev;
struct spi_device *spi;

struct semaphore lock;
};
来描述我的设备附加信息
先理下逻辑:
[1]module_init(spi_ad7888_init);


[2]static int __init spi_ad7888_init(void) {}(在这个函数里应实现 初始化主设备号及字符设备注册最后调用平台设备注册函数spi_register_driver(spi_ad7888_driver))

[3]关于struct spi_driver spi_ad7888_driver这个结构体,需要填写的项有.probe .remove .driver其中.driver 又包含.name  .owner 等等。其中.name这个是关键,平台设备驱动程序的匹配就是根据这个name和device里面的.name作比较,当然,这里有点不同,spi的match会调到后面讲的spi_board_info中的.modalias做比较。

[4]接着填写probe和remove函数,我在spi_ad7888_driver结构体中,将.probe=spi_ad7888_probe。那么spi_ad7888_probe中实现的功能有:
分配一个struct ad7888结构体空间,这个结构体在上面定义
注:以下省略了所有的出错处理~实际应用中不能缺少。
static int __devinit spi_ad7888_probe(struct spi_device *spi) {
struct * chip;
chip =kmalloc(sizeof(struct ad7888),GFP_KERNEL);
dev_set_drvdata(&spi->dev,chip);//设置这里的指针是为了后面调用方面
chip->spi = spi;
init_MUTEX(&chip->lock);//初始化信号量
cdev_init(&chip->cdev,&ad_fop) ;
cdev_add(&chip->cdev,devno,1);//这里的devno为MKDEV(100,0);
}
至此probe的函数实现完了,应该要填写struct file_operations ad_fop这个结构体了,和所有字符设备一样
这里还是那些open ,read,write,ioctl,release这几个基本操作~接合实际硬件,对于这个spi ad 7888多通道的芯片来说,write的功能的实现还不如去实现ioctl,所以我这里省去了write的实现。

[5]struct file_operations ad_fop={
.owner = THIS_MODULE,
.ioctl = ad_ioctl,
.read = ad_rd,
.write = ad_wr,
.open = ad_op,
.release = ad_ex,
};
先看看open函数
static int ad_op(struct inode *inode,struct file *filp)
{
struct ad7888 *chip = container_of(inode->i_cdev,struct ad7888,cdev);
struct spi_device *spi = chip->spi;

filp->private_data=chip;//这里设置了为了read能找到ad7888这个结构,很重要哦!

return 0;
}
接下来要一个一个填写,其他的功能函数我就省略了,只实现一个read函数,其他依次类推,应该很容易弄出来。
[6]static int ad_rd(struct file *filp,int __user *buf,size_t count,loff_t *offset){
struct ad7888 *chip=filp->private_data;//还记得前面那个open函数里说明的吗?

if(down_interruptible(&chip->lock))//控制并发,互斥。如果没其他应用程序调用,其实也可以省去,毕竟现在的目的只是要让芯片简单地工作起来
return -ERESTARTSYS;

spi_read_buf[0]=0;
spi_read_buf[1]=0;
spi_write_then_read(chip->spi,spi_transfer_buf,2,spi_read_buf,2);//为什么发两个字节过去?
//看过2440 spi data寄存的朋友都知道,它只有8字节的单位,现在我们的ad7888采集回来的精度是12位,一次传送肯定不够的~发送两个字节过去,spi_write_then_read会将12位的长度截成两个字节~到后来还得拼回去,我在驱动层没做~在应用层调用的时候,我是用tmp|=buf[0];tmp|=buf[1];或了两次拼回去。
if(copy_to_user(buf,spi_read_buf,sizeof(spi_read_buf)))
{
//up(&chip->rdlock);
up(&chip->lock);
return -EFAULT;
}
up(&chip->lock);
return 0;

}

好了,驱动程序写完了,编译完成后~就能用了吗?当然不行了~还有一件重要的事情没做~就是spi_device是什么时候生成的啊?
上面的函数里面只有struct *spi_device 连 struct ad7888这个自定义的函数里面也只有struct *spi_device 并没有分配它的空间,那么它在哪里实现?


linux的设备管理模型是很巧妙的~
在linux-2.6.24/arch/arm/mach-s3c2440/ 的mach-smdk2440.c

有这么一段:static struct spi_board_info spi_board_info[] __initdata =
{
{
.modalias               = "ad7888",//你注意到了没?这就是前面我说dirver中.name要设置相同的地方
.chip_select    = 0,
/*.controller_data =AT91_PIN_PA3 ,*/
//.max_speed_hz = 5000*24, /*(4700 * 26)-125000 * 26, (max sample rate @ 3V) * (cmd + data + overhead) */
.max_speed_hz   = 120000, /*(4700 * 26)-125000 * 26, (max sample rate @ 3V) * (cmd + data + overhead) */
.bus_num                = 0,

},
};
OK再在static void __init smdk2440_machine_init(void)
{
……
spi_register_board_info(spi_board_info, ARRAY_SIZE(spi_board_info));//把这句话加上,系统在启动时自动分配一个device
/*spi控制器初始化的时候,会使用spi_board_info生成spi_device
sslspi_probe()        
spi_register_master()
scan_boardinfo()   // 使用spi_board_info信息生成新的spi_device
spi_new_device()
*/
……
}
好了,到这里~如果你的内核中打开了spi master控制器的话,编译驱动添加到内核,就可以实现读取spi的驱动了。。。

AD7888与S3C2410的SPI接口及Linux下嵌入式驱动的实现.pdf 立即下载

相关推荐

STM32 读写AD7888程序

AD7888 stm32程序 详细的书写了SPI 读写AD7888,程序已测试,完全可以使用

STM32(十三)- SDIO_SD读写测试

7 转载于:https://www.cnblogs.com/wuguangzong/p/10925031.html

weixin_30784945的博客 239

AD7888手册中文版

AD7888技术手册中文版,基于原英文版进行汉化,比较准确。

S3C2440驱动移植——SPI

PC主机:Ubuntu 10.4 和redhat 9.0 目标板:TQ2440开发板,linux内核2.6.30 本文将从原理上讲解如何进行SPI驱动移植。作者希望在移植SPI驱动之前,您已对SPI子系统有所了解。 本文的讲解将基于我所写的spi子系统分析: 基于S3C2440的嵌入式Linux驱动——SPI子系统解读(一) 基于S3C2440的嵌入式Linux驱动——S

wqc02的专栏 6232

Visionpro 使用CogPMAlignTool工具提取匹配轮廓点

【代码】Visionpro 使用CogPMAlignTool工具提取匹配轮廓点。

weixin_43947315的博客 2091

3D slicer查看形变场

3Dslice显示数据和形变场 将需要可视化的数据也导入3Dslice,注意描述为volume(图1a),如果没有显示是因为路径中不能有中文,也就是moving图像。 (b) 图1、数据导入形变场和 将相应的形变场数据直接拖入3Dslice,并修改描述为形变场(图1b)。 点击transform菜单图2(a),然后勾选形变场可视化图2(b)。 (b) 图2...

fanre的专栏 1947

STM32与S3C2440的区别

在学习嵌入式的路上,我们可能会接触到这两个比较典型的MCU。其中最大的区别就是S3C2440能跑linux操作系统,常常作为学习嵌入式linux的硬件平台。可能大家会问既然S3C2440能跑linux操作系统,似乎比stm32厉害多了,为什么不直接去学习S3C2440呢? 下面我就大概解释一下大家遇到的困惑:1.先来说说stm32 stm32是ST公司推出的基于Cortex-M3内核的

耶律大石的博客 2万+

EDEM-fluent耦合时出现的问题及摸索出的解决办法及DPM颗粒信息导出

EDEM-fluent耦合时出现的问题及摸索出的解决办法及DPM颗粒信息导出

zheaa的博客 5748

mini2440 spi驱动总结

最近在参考了很多的网络资源后编写调试了在mini2440开发板上的SPI驱动程序,因为急于使用也没有分析S3C2440在Linux下自带的源程序,编写程序的方式较为简单,只是实现了简单的字符发送和接收。        主要是配置一些寄存器。这些寄存器的配置按照S3C2440手册

gflytu的专栏 2254

手把手教你学Simulink——基于核能发电系统的场景实例:核电站汽轮机与发电机协调控制仿真

在核电机组中,汽轮机-发电机系统是将核能最终转化为电能的关键环节。与火电机组不同,核反应堆的热惯性大、功率调节慢,因此汽轮机的控制策略需与反应堆特性相匹配,实现安全、稳定、高效的能量转换。维持发电机输出功率/频率稳定(响应电网调度)调节汽轮机进汽压力(匹配一回路蒸汽供应)实现“堆-机协调控制”保障机组在启停、变负荷、甩负荷等工况下的安全性本教程将带你使用构建一个核电站汽轮机与发电机协调控制系统汽轮机调速系统(调门控制)建模发电机与电网接口建模(同步机)

MHD0815的博客 212

智慧社区:安防系统解决方案

智慧社区:安防系统解决方案

s13166803785的博客 431

S3C2440驱动移植——SPI .

PC主机:Ubuntu 10.4 和redhat 9.0 目标板:TQ2440开发板,linux内核2.6.30 本文将从原理上讲解如何进行SPI驱动移植。作者希望在移植SPI驱动之前,您已对SPI子系统有所了解。 本文的讲解将基于我所写的spi子系统分析: 基于S3C2440的嵌入式Linux驱动——SPI子系统解读(一) 基于S3C2440的嵌入式Linux驱动——S

孔祥锐 924

数据结构课程设计--基于知识图谱的中医食疗数据管理与应用

【代码】数据结构课程设计--基于知识图谱的中医食疗数据管理与应用。

M_inherit的博客 565

S3C2440 Linux驱动移植——SPI

PC主机:Ubuntu 10.4 目标板:TQ2440开发板,linux内核2.6.32 本文将从原理上讲解如何进行SPI驱动移植。作者希望在移植SPI驱动之前,您已对SPI子系统有所了解。 本文的讲解将基于我所写的spi子系统分析: 基于S3C2440的嵌入式Linux驱动——SPI子系统解读(一) 基于S3C2440的嵌入式Linux驱动——SPI子系统解读(二)

嵌入式Linux 4810

基于C++的MMC仿真与PSCAD建模

MMC是一种基于模块化设计的多电平换流器,通常由若干个基本单元(子模块)组成。每个子模块包含一个电容和两个开关,通过控制开关的开闭,实现输出电压的级联和多电平化。高效率:由于开关频率较低,开关损耗小,效率高。高可靠性:模块化设计使得故障模块易于更换,提高了系统的可靠性。灵活扩展:可以通过增加或减少子模块的数量,方便地调整输出电压和功率。PSCAD(Power Systems Computer Aided Design)是广泛应用于电力系统仿真的专业软件,具有强大的建模和仿真能力。

qq_38334677的博客 295

LSTM+Transformer混合模型时间序列预测实战教学

LSTM+Transformer混合模型时间序列预测实战教学 毕业设计

8218

mini2440移植之SPI驱动

目标平台:mini2440 内核:linux2.6.32.2 1,修改arch/arm/mach-s3c2440/mach-mini2440.c文件,加入头文件: #include #include 然后加入如下代码: static struct spi_board_info s3c2410_spi0_board[] = { [0] = {

tong646591的专栏 823

IEM模型,地表粗糙度模拟

利用微波反演土壤水分时候,利用IEM模型可以实现对随机地表的模拟

RC522 SPI驱动 mini2440

参考http://blog.youkuaiyun.com/fulinus/article/details/40898677The MFRC522 has the capability to perform a digital self test. The self test is started by using the following procedure: 1. Perform a soft rese

Welcome 2955

jdk-8u144-linux-x64.tar.gz.

该资源为搭建Linux系统Java环境用,资源的后缀为tar.gz格式的压缩包

深度学习工具包 DeepLearningToolbox

深度学习工具包Deprecation notice.-----This toolbox is outdated and no longer maintained.There are much better tools available for deep learning than this toolbox, e.g. [Theano](http://deeplearning.net/software/theano/), [torch](http://torch.ch/) or [tensorflow](http://www.tensorflow.org/)I would suggest you use one of the tools mentioned above rather than use this toolbox.Best, Rasmus.DeepLearnToolbox================A Matlab toolbox for Deep Learning.Deep Learning is a new subfield of machine learning that focuses on learning deep hierarchical models of data.It is inspired by the human brain&#39;s apparent deep (layered, hierarchical) architecture.A good overview of the theory of Deep Learning theory is[Learning Deep Architectures for AI](http://www.iro.umontreal.ca/~bengioy/papers/ftml_book.pdf)For a more informal introduction, see the following videos by Geoffrey Hinton and Andrew Ng.* [The Next Generation of Neural Networks](http://www.youtube.com/watch?v=AyzOUbkUf3M) (Hinton, 2007)* [Recent Developments in Deep Learning](http://www.youtube.com/watch?v=VdIURAu1-aU) (Hinton, 2010)* [Unsupervised Feature Learning and Deep Learning](http://www.youtube.com/watch?v=ZmNOAtZIgIk) (Ng, 2011)If you use this toolbox in your research please cite [Prediction as a candidate for learning deep hierarchical models of data](http://www2.imm.dtu.dk/pubdb/views/publication_details.php?id=6284)```@MASTERSTHESIS\{IMM2012-06284, author = "R. B. Palm", title = "Prediction as a candidate for learning deep hierarchical models of data", year = "2012",}```Contact: rasmusbergpalm at gmail dot comDirectories included in the toolbox-----------------------------------`NN/` - A library for Feedforward Backpropagation Neural Networks`CNN/` - A library for Convolutional Neural Networks`DBN/` - A library for Deep Belief Networks`SAE/` - A library for Stacked Auto-Encoders`CAE/` - A library for Convolutional Auto-Encoders`util/` - Utility functions used by the libraries`data/` - Data used by the examples`tests/` - unit tests to verify toolbox is workingFor references on each library check REFS.mdSetup-----1. Download.2. addpath(genpath(&#39;DeepLearnToolbox&#39;));Example: Deep Belief Network---------------------```matlabfunction test_example_DBNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit RBM and visualize its weightsrand(&#39;state&#39;,0)dbn.sizes = [100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);figure; visualize(dbn.rbm{1}.W&#39;); % Visualize the RBM weights%% ex2 train a 100-100 hidden unit DBN and use its weights to initialize a NNrand(&#39;state&#39;,0)%train dbndbn.sizes = [100 100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);%unfold dbn to nnnn = dbnunfoldtonn(dbn, 10);nn.activation_function = &#39;sigm&#39;;%train nnopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.10, &#39;Too big error&#39;);```Example: Stacked Auto-Encoders---------------------```matlabfunction test_example_SAEload mnist_uint8;train_x = double(train_x)/255;test_x = double(test_x)/255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit SDAE and use it to initialize a FFNN% Setup and train a stacked denoising autoencoder (SDAE)rand(&#39;state&#39;,0)sae = saesetup([784 100]);sae.ae{1}.activation_function = &#39;sigm&#39;;sae.ae{1}.learningRate = 1;sae.ae{1}.inputZeroMaskedFraction = 0.5;opts.numepochs = 1;opts.batchsize = 100;sae = saetrain(sae, train_x, opts);visualize(sae.ae{1}.W{1}(:,2:end)&#39;)% Use the SDAE to initialize a FFNNnn = nnsetup([784 100 10]);nn.activation_function = &#39;sigm&#39;;nn.learningRate = 1;nn.W{1} = sae.ae{1}.W{1};% Train the FFNNopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.16, &#39;Too big error&#39;);```Example: Convolutional Neural Nets---------------------```matlabfunction test_example_CNNload mnist_uint8;train_x = double(reshape(train_x&#39;,28,28,60000))/255;test_x = double(reshape(test_x&#39;,28,28,10000))/255;train_y = double(train_y&#39;);test_y = double(test_y&#39;);%% ex1 Train a 6c-2s-12c-2s Convolutional neural network %will run 1 epoch in about 200 second and get around 11% error. %With 100 epochs you&#39;ll get around 1.2% errorrand(&#39;state&#39;,0)cnn.layers = { struct(&#39;type&#39;, &#39;i&#39;) %input layer struct(&#39;type&#39;, &#39;c&#39;, &#39;outputmaps&#39;, 6, &#39;kernelsize&#39;, 5) %convolution layer struct(&#39;type&#39;, &#39;s&#39;, &#39;scale&#39;, 2) %sub sampling layer struct(&#39;type&#39;, &#39;c&#39;, &#39;outputmaps&#39;, 12, &#39;kernelsize&#39;, 5) %convolution layer struct(&#39;type&#39;, &#39;s&#39;, &#39;scale&#39;, 2) %subsampling layer};cnn = cnnsetup(cnn, train_x, train_y);opts.alpha = 1;opts.batchsize = 50;opts.numepochs = 1;cnn = cnntrain(cnn, train_x, train_y, opts);[er, bad] = cnntest(cnn, test_x, test_y);%plot mean squared errorfigure; plot(cnn.rL);assert(er<0.12, &#39;Too big error&#39;);```Example: Neural Networks---------------------```matlabfunction test_example_NNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);% normalize[train_x, mu, sigma] = zscore(train_x);test_x = normalize(test_x, mu, sigma);%% ex1 vanilla neural netrand(&#39;state&#39;,0)nn = nnsetup([784 100 10]);opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samples[nn, L] = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.08, &#39;Too big error&#39;);%% ex2 neural net with L2 weight decayrand(&#39;state&#39;,0)nn = nnsetup([784 100 10]);nn.weightPenaltyL2 = 1e-4; % L2 weight decayopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, &#39;Too big error&#39;);%% ex3 neural net with dropoutrand(&#39;state&#39;,0)nn = nnsetup([784 100 10]);nn.dropoutFraction = 0.5; % Dropout fraction opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, &#39;Too big error&#39;);%% ex4 neural net with sigmoid activation functionrand(&#39;state&#39;,0)nn = nnsetup([784 100 10]);nn.activation_function = &#39;sigm&#39;; % Sigmoid activation functionnn.learningRate = 1; % Sigm require a lower learning rateopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, &#39;Too big error&#39;);%% ex5 plotting functionalityrand(&#39;state&#39;,0)nn = nnsetup([784 20 10]);opts.numepochs = 5; % Number of full sweeps through datann.output = &#39;softmax&#39;; % use softmax outputopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, &#39;Too big error&#39;);%% ex6 neural net with sigmoid activation and plotting of validation and training error% split training data into training and validation datavx = train_x(1:10000,:);tx = train_x(10001:end,:);vy = train_y(1:10000,:);ty = train_y(10001:end,:);rand(&#39;state&#39;,0)nn = nnsetup([784 20 10]); nn.output = &#39;softmax&#39;; % use softmax outputopts.numepochs = 5; % Number of full sweeps through dataopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, tx, ty, opts, vx, vy); % nntrain takes validation set as last two arguments (optionally)[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, &#39;Too big error&#39;);```[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/rasmusbergpalm/deeplearntoolbox/trend.png)](https://bitdeli.com/free "Bitdeli Badge")

《Linux驱动:s3c2410/s3c2440 ts驱动分析》

前面结合“平台总线-设备-驱动”模型分析了ts(触摸)驱动的加载过程,现在进入驱动程序分析下其具体的实现。涉及到输入子系统、s3c2440的ADC转换和触摸控制器。

qq_40709487的博客 716

廖雪峰Python教程[完整版]

廖雪峰的python教程整理的,已经排好版了,而且还是完整的,适合小白

s3c2440上MMC/SD卡驱动的分析(一)

一、开发环境 主  机:VMWare--Fedora 9 开发板:Mini2440--64MB Nand, Kernel:2.6.30.4 编译器:arm-linux-gcc-4.3.2 二、MMC/SD介绍及SDI主机控制器   首先我们来理清几个概念: MMC:(Multi Media Card)由西门子公司和首推CF的SanDisk于1997年推出的多媒体

阿戟哥哥的专栏 1666

嵌入式Linux之我行——S3C2440上MMC/SD卡驱动实例开发讲解(一)

http://bbs.ednchina.com/BLOG_ARTICLE_1787530.HTM 嵌入式Linux之我行——S3C2440上MMC/SD卡驱动实例开发讲解(一) 发布时间:2010-06-29 10:40:34 技术类别:生活点滴     http://blog.chin

ahaochina的专栏 789

S3C2440上 MMC/SD卡驱动实例开发讲解(一)

一、开发环境主  机:VMWare--Fedora 9开发板:Mini2440--64MB Nand, Kernel:2.6.30.4编译器:arm-linux-gcc-4.3.2二、MMC/SD介绍及SDI主机控制器  首先我们来理清几个概念:MMC:(Multi Media Card)由西门子公司和首推CF的SanDisk于1997年推出的多媒体记忆卡标准。SD:(Secure Digital Memory Card)由日本松下、东芝及美国SanDisk公司于1999年8月共同开发研制的新一

1069
上一篇: Framebuffer, 原理
下一篇: 采用AT88SC系列芯片进行系统加密的原理
control_tech
博客等级 码龄17年 53粉丝 32原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值