BLE核心模块FS-QN9021模块开发-linux版

这段时间又参与了一个新的小项目,简单概括为蓝牙、智能、家居吧,虽然时间有点紧,还是希望能把这一些东西记录下来。


####BLE 什么是BLE?参考这篇文章做如下总结。 
中文名称为蓝牙低功耗。主要特点为低成本、超低功耗、短距离、标准接口和可互操作性强,并且工作在免许可的2.4GHz ISM射频段,需要支持蓝牙4.0(系统为Android4.3及以上)的主机设备才能与其连接。

目前生产BLE芯片的厂家主要有CSR、TI、Nodic和NXP(QN902x),各个厂家芯片对比如下图

s

从如上图对比可以看出,NXP的QN902x在功耗方面比CSR和TI更省电,在接收灵敏度和模式方面比Nodic的胜一筹,它的从设备相比其它几家可以连接的更多,共有8个,这也算是蓝牙4.0的一大特色吧,并且NXP的芯片已经过了MFI认证,直接能与苹果设备相连接,因为这种认证也是挺贵的。

因为BLE的低功耗、低成本及强大的处理能力,并且随着iPhone的设备支持蓝牙4.0,BLE的终端设备在我们的生活当中将会越来越多,在未来将会有爆发式增长。

QN902X是一款内核为M0的蓝牙BLE SOC芯片,其SDK对蓝牙BLE的profile都有实现,并提供源码,SDK也提供很多工具以便使用芯片,比如引脚配置,NVDS读写,串口USB Dongle(配合上位机可以调试各profile,没有手机也可以调试),ISP下载等,但QN902X不提供数据手册,所有的外设操作都以库的方式提供,SDK说明比较全面但全是英文的。

与NODRDIC的51822和TI的CC2540不同QN902X的架构是M0+ROM+FLASH+SRAM的方式,其中ROM放的是蓝牙协议和内部一个小的调度核,FLASH放的是用户程序和数据,RAM用于跑程序。其中ROM:96K,SRAM:64K,FLASH:64K/128K。因为QN902X程序是跑到SRAM中,所以它的深度睡眠电流比较大些。


####环境搭建 老生长谈,开发一款产品,第一步当然是搭建开发环境咯。

#####wine安装 由这里可知,keil不支持linux环境,所以必须自己想办法了。这是在linux环境下运行windows的程序,本身使用windows的请自动忽略这一步。具体请看官网/博文

~ $ sudo add-apt-repository ppa:ubuntu-wine/ppa
~ $ sudo apt-get update
# => 我的是64为的系统,所有安装64位版
~ $ sudo apt-get install wine1.8-amd64
# => 配置
~ $ winecfg
# => 中文路径:~/ .wine/drive_c/windows/Fonts/
~ $ winetricks corefonts

#####keil安装 首先得下载keil,可以自己去官网下载最新的,也可以直接点击mdk5.17下载地址,参考这篇博文并实践做如下记录。

# => 进入下载目录安装
~ $ wine mdk517.exe

提示:卸载程序可以用wine uninstaller 
如编译有限制,下载破解注册机Keygen:http://pan.baidu.com/s/1hqGSRqs

安装完成后,会弹出来一个安装器件(pack installer)如下的界面,也就是说,你要用它来开发哪个芯片(此项目可以忽略,后面步骤导入DB)。 packin


或者打开keil界面会看到如下图标 packicon


要开发哪一款芯片,点击install即可,或者先网页端下载好在导入,具体参考上面提供的博文地址。

#####MCU DB库安装 下载Quintic最新的SDKQBlue1.3.7,安装:

~ $ wine QBlue-1.3.7.exe

会弹出窗口如下,点击安装即可,或者打开桌面QBlue里的QN9020DevDBforIDE工具安装。

sdk

#####keil使用 首先获取开源代码

~ $ cd
~ $ git clone git@bitbucket.org:T-Firefly/fireble.git

桌面打开keil,假如我们希望开始proxr工程:

  1. 在keil的Project菜单中选择Open Project…
  2. 弹出文件选择框中,打开/home/xxx/fireble/BLE/prj_proxr/keil/proxr.uvproj工程文件(linux可能在z磁盘里)
  3. 配置DB如下,如果没发现库,请重启软件

    dbset

  4. 编译代码,成功后如下,具体配置选项请参考这里

    compile

  5. 下载程序,下载的时候需要按复位键,如下载出错,可先下载到SRAM。

    dl

Tip:在ubuntu 上串口识别为ttyS0或ttyUSB0之类,在wine上识别不到,可用:

# =>将其该为小写的com1,如果不行,将其改为大写的COM1
~ $ sudo ln -s /dev/ttyUSB0 ~/.wine/dosdevices/com1
~ $ sudo usermod -a -G dialout $USER
~ $ sudo chmod 777 ~/.wine/dosdevices/com1

即可在wine的应用程序使用串口


####项目实践 #####点亮LED 对于新的芯片与开发板,从LED实验开始。首先下载该开发板的原理图,对该LED部分的电路进行分析。

led

本程序非常简单,复制gpio demo代码

gpio

实现让开发板D1灯闪烁如下:

/* Set pin D1/P2_7 */
gpio_set_direction_field(GPIO_P27, (uint32_t)GPIO_OUTPUT);
while(1)
{
	gpio_write_pin_field(GPIO_P27, GPIO_LOW);
	delay(100000);
	gpio_write_pin_field(GPIO_P27, GPIO_HIGH);
	delay(100000);
}

程序流程:系统初始化–>GPIO配置–>各驱动模块初始化–>主循环实现功能 
效果如下:

led

#####UART实验 串口通信可以用来打印数据,调试程序,有必要实验一下。 
同样复制uart demo代码 
改程序如下:

//Print out "Hello NXP!\n" thought uart.
uart_printf(QN_UART0, (uint8_t *)"Hello NXP!\n");
uart_printf(QN_UART0, (uint8_t *)"This is nephen's test!\n");

波特率设置为115200,现象如下: uart_tst

#####PWM实验 由于这个项目会控制到电机什么的,所以pwm少不了,这个实验是通过pwm控制陶瓷蜂鸣器报警和呼吸灯。通过PWM方式调节脉冲频率和占空比,变换LED亮度,渐变亮度实现呼吸灯效果。FireBLE板载的贴片蜂鸣器是压电式陶瓷蜂鸣器,压电陶瓷蜂鸣器要想响起来,需要满足四个条件:多谐振荡器、压电蜂鸣片、阻抗匹配器及共鸣箱,压电蜂鸣片由锆钛酸铅或铌镁酸铅压电陶瓷材料制成,在陶瓷片的两面镀上银电极,经极化和老化处理后,再与黄铜片或不锈钢片粘在一起。板载的蜂鸣器缺少的只是多谢振荡器,这里我们用PWM来代替,输出1.5KHz-2.5KHz的方波信号推动压电蜂鸣片发声,频率在1.5KHz-2.5KHz才会响,太高太低都不响,直接加3.3V更不会响,直接加3.3V响的那是电磁式蜂鸣器,有源和无源。 
首先看下电路连接

buzz

同样复制pwm代码,改写如下:

int main (void)
{
    SystemInit();

    pwm_init(PWM_CH0);
    pwm_io_config();
    //P2.7 will output pwm wave with period for 1000us and pulse for 400us
    pwm_config(PWM_CH0, PWM_PSCAL_DIV, PWM_COUNT_US(1000, PWM_PSCAL_DIV), PWM_COUNT_US(500, PWM_PSCAL_DIV));
    pwm_enable(PWM_CH0, MASK_ENABLE);
    pwm_io_dis_config();

    pwm_init(PWM_CH1);
    pwm_io_config();
    //P2.6 will output pwm wave with period for 1000us and pulse for 500us
    pwm_config(PWM_CH1, 119, PWM_COUNT_US(1000, 119), PWM_COUNT_US(500, 119));
    pwm_enable(PWM_CH1, MASK_ENABLE);

    while (1)                                /* Loop forever */
    {
			int i;
			for(i=0;i<=1000;i++)
			{
				pwm_config(PWM_CH1, 119, PWM_COUNT_US(1000-i, 119), PWM_COUNT_US(500, 119));
				if(i%2)
					pwm_config(PWM_CH0, PWM_PSCAL_DIV, PWM_COUNT_US(1000-i, PWM_PSCAL_DIV), PWM_COUNT_US(500, PWM_PSCAL_DIV));
				else
					pwm_config(PWM_CH0, PWM_PSCAL_DIV, PWM_COUNT_US(i, PWM_PSCAL_DIV), PWM_COUNT_US(500, PWM_PSCAL_DIV));
				delay(2000);
			}
    }
}

呼吸灯效果:

breath

#####按键广播 看到有些人对买的fireBLE按键广播不懂,其实刚开始我也是这样的,摸不着头脑,现在至少不会太迷糊,就给大家记录下吧。 
首先按键按下属于gpio中断,找到中断初始化函数:SystemInit(); ——》 
gpio_init(gpio_interrupt_callback); ——》 
void gpio_interrupt_callback(enum gpio_pin pin) ——》 
void usr_button1_cb(void) ——》 
ke_evt_set(1UL « EVENT_BUTTON1_PRESS_ID); 找到EVENT_BUTTON1_PRESS_ID对应的事件,搜索 EVENT_BUTTON1_PRESS_ID,找到void usr_init(void)里的if(KE_EVENT_OK != ke_evt_callback_set(EVENT_BUTTON1_PRESS_ID, app_event_button1_press_handler))可知为app_event_button1_press_handler ——》 
ke_timer_set(APP_KEY_SCAN_TIMER,TASK_APP,2); 同样通过搜索APP_KEY_SCAN_TIMER可知定时器事件为app_key_scan_timer_handler ——》 
app_key_scan_timer_handler,这里进行adc采集,完成后由adc_read(&read_cfg, adc_key_value, KEY_SAMPLE_NUMBER, adc_test_cb);可知进入adc_test_cb,在这里设置了EVENT_ADC_KEY_SAMPLE_CMP_ID,搜索找到对应事件app_event_adc_key_sample_cmp_handler ——》 
app_event_adc_key_sample_cmp_handler,判断按键朝哪个方向按,定时回调 ——》 
app_key_process_timer_handler,如下:

int app_key_process_timer_handler(ke_msg_id_t const msgid, void const *param,
                                  ke_task_id_t const dest_id, ke_task_id_t const src_id)
{
	ke_evt_clear(1UL << EVENT_ADC_KEY_SAMPLE_CMP_ID);
	switch (key_value0)
	{

	case	key_up:
		if (APP_IDLE == ke_state_get(TASK_APP))
		{

			// start adv
			//QPRINTF("you press up key\r\n!");
			app_gap_adv_start_req(GAP_GEN_DISCOVERABLE | GAP_UND_CONNECTABLE,
			                      app_env.adv_data, app_set_adv_data(GAP_GEN_DISCOVERABLE),
			                      app_env.scanrsp_data, app_set_scan_rsp_data(app_get_local_service_flag()),
			                      GAP_ADV_FAST_INTV1, GAP_ADV_FAST_INTV2);

这里一看就明白,向上按时,进入广播。其实在系统上电的时候,已经进行了一系列的gap建立连接的初始话,就差广播了,所以这里只要广播出去就能和别的蓝牙建立连接。

#####QTool使用及蓝牙了解 使用QBlueStudio中QTool工具进行蓝牙开发分析,可以方便的对各个蓝牙操作的过程进行细致的研究,并结合具体的源代码进行查看,能够更加深入的了解到蓝牙协议的实现过程,大部分的API接口在GAP和GATT。具体的使用文档请查看QBlueStudio里的Document。Linux里可以采取这种方法查看:地址见/home/username/.wine/drive_c/QBlue/QN9020/QBlue-1.3.7/Documents,然后使用evince命令打开。

在这之前,还需对蓝牙的一些术语做一个大概的了解,比如什么是master,slave,主机。客户端与服务器又是什么。GAP与GATT有什么区边。蓝牙各个协议层都有哪些分工。下面参考做一些归纳:

  1. BLE规范中定义了GAP(Generic Access Profile)和GATT(Generic Attribute)两个基本配置文件。 
    a.协议中的GAP层负责设备访问模式和进程,包括设备发现,建立连接,终止连接。初始化安全特性和设备配置。 
    b.GATT层用于已连接的蓝牙设备之间的数据通信。GATT通俗理解为用于主从机之间的客户端和服务器端的数据交互,以Attribute Table来体现。

  2. BLE低功耗蓝牙中有四种设备类型,Central主机,Peripheral从机,Observer观察者,Broadcaster广播者。通常Central主机,Peripheral从机一起使用,Observer观察者,Broadcaster广播者一起使用。Central和Peripheral连接交换数据,平时我们使用到的基本上是这种模式。而像多温度采集器,通常使用Observer和Broadcaster这种无连接形式。 
    主机和从机是这样开机工作的:从机开始广播,然后主机扫描广播的从机,当从机收到主机的扫描请求后,会向主机发送扫描回应数据。然后主机发起连接,然后开始通讯。所以从机需要设置广播内容和扫描回应内容。这方面的代码可以查看App_gap.c和App_gap_task.c。
    • profile:可以理解为一种规范,一个标准的通信协议,profile存在于从机中。蓝牙组织规定了一系列的标准profile,如防丢计、心率计。每个profile中包含多个service,每个service代表从机的一种能力。
    • Service:可以理解为一种服务,在BLE从机里,通过有多个服务,例如电量信息服务、系统信息服务等。每个service又包含多个characteristic特征值。每个具体的characteristic特征值才是BLE通讯的主体,比如当前电量是80%。所以会通过电量的characteristic特征值特征值保存在从机的profile里,这样主机就可以通过这个characteristic来读取80%这个数。
    • characteristic:characteristic特征值,BLE主从机均是通过characteristic来实现,可以理解为一个标签,通过这个标签可以获取或者写入想要的内容。
    • UUID:统一标识吗,我们刚提到的characteristic和service,都需要一个唯一的UUID来标识。

    每个从机都会有个叫做profile的东西存在,不管自定义的还是标准的profile,他们都是由一系列的Service组成,然后每个service又包含多个characteristic,主机和从机之间的通信,均是通过characteristic来实现。 
    BLE协议栈中传输数据分为两方面,一个GATT的client主动向service发送数据。另一个是GATT的service主动向client发送数据。即主从之前相互传数据。 
    主机向从机发送数据,使用GATT_Write 
    从机向主机发送数据,使用GATT_Notification

  3. GATT有Service和Client,Service作为服务器端,对GATT Client提供read/write接口,一般情况下,Central作为Client,Peripheral作为Service。所以主机会调用read/write来和作为Service端的Peripheral从机通讯。而Peripheral则通过notify的方式即调用GATT_Notifycation发起和主机通讯。
  4. 特征值声明值可以有五种属性:Read(可读) Write(可靠的可写,带响应) Write without resp(不可靠的可写,不带响应) Indicate(可靠通知,带响应) Notify(不可靠通知,不带响应)

更多请查看蓝牙设计问与答 /Android 蓝牙4.0 BLE 理解

BLE中主从机建立连接,到配对和绑定的过程如下图。 

#####QPPS工程 在此之前,建议先了解一下Firefly的QPPS介绍/对QPPS profile中服务和特征实现的分析理解/对profile QPPS的分析理解。关于蓝牙的数据收发部分可以看Firefly的协议栈介绍。数据帧格式如下:

Tip:技术案例中串口透传案例,实现的是将蓝牙模组串口所接收到的数据透传到app,并且把app的数据通过蓝牙透传到串口输出,案例本身的实现是针对串口通信和蓝牙透传的结合。

  1. 其实BLE完成初始化的条件并不是进入main函数的while(1)中(系统在调度了几个消息之后才完成的初始化,前几次进入while(1)时初始化都是没完成的),真正完成初始化并且可以运行是在打印BLE is Ready这句话的地方。那个地方你可以看到只要开启QN_DEMO_AUTO宏定义就可以上电广播了。
  2. 设备名默认从NVDS中写入,但是软件也可以修改,可以用app_gap_set_devname_req修改设备名。
  3. 如果你非要用软件指定,参考广播内容设定,取消NVDS读取,直接利用app_gap_set_devname_req函数指定设备名,然后将QN_LOACL_NAME广播出去,那么设备名和广播中的设备名都会是QN_LOCAL_NAME了。

    具体实施如下

    修改自动广播,无需向上拨动按键:

    c #=> App_config.h中,取消如下的注释即可 #define QN_DEMO_AUTO 1 在广播之前改变设备的名字,注意app_set_adv_data函数

    c //Set remote device name app_gap_set_devname_req("nephen",6); // Created DB should has been finished by each profile service, // Start Adv mode automatically here app_gap_adv_start_req(GAP_GEN_DISCOVERABLE|GAP_UND_CONNECTABLE, app_env.adv_data, app_set_adv_data(GAP_GEN_DISCOVERABLE), app_env.scanrsp_data, app_set_scan_rsp_data(app_get_local_service_flag()), GAP_ADV_FAST_INTV1, GAP_ADV_FAST_INTV2);

  4. 接收手机app中9600可写属性发送的数据。 
    由下面Qpps_task.c可知,当有数据到达蓝牙时,会触发gatt_write_cmd_ind_handler,同理,发送数据触发qpps_data_send_req_handler,得到通知触发gatt_notify_cmp_evt_handler,app层对应的api是app_qpps_data_send_cfm_handlerapp_qpps_data_ind_handlerapp_qpps_cfg_indntf_ind_handler,下文会提到,只会对这些做修改

    c /// Connected State handler definition. const struct ke_msg_handler qpps_connected[] = { {QPPS_DATA_SEND_REQ, (ke_msg_func_t) qpps_data_send_req_handler}, {GATT_WRITE_CMD_IND, (ke_msg_func_t) gatt_write_cmd_ind_handler}, {GATT_NOTIFY_CMP_EVT, (ke_msg_func_t) gatt_notify_cmp_evt_handler}, }; 而在gatt_write_cmd_ind_handler函数里,会对QPPS_IDX_RX_DATA_VAL属性进行处理

    ```c else if (param->handle == (qpps_env.shdl + QPPS_IDX_RX_DATA_VAL)) { if (param->length <= QPP_DATA_MAX_LEN) { //inform APP of configuration change struct qpps_data_val_ind * ind = KE_MSG_ALLOC_DYN(QPPS_DAVA_VAL_IND, qpps_env.appid, TASK_QPPS, qpps_data_val_ind, param->length);

                 memcpy(&ind->conhdl, &(qpps_env.conhdl), sizeof(uint16_t));
                 //Send received data to app value
                 ind->length = param->length;
                 memcpy(ind->data, param->value, param->length);
    
                 ke_msg_send(ind);
             }
             else
             {
                 status = QPPS_ERR_RX_DATA_EXCEED_MAX_LENGTH;
             }
         }  ```
    

    再由上面的QPPS_DAVA_VAL_IND可知会跳到下面这个函数,在这里对接收到的数据进行处理

    ```c int app_qpps_data_ind_handler(ke_msg_id_t const msgid, struct qpps_data_val_ind *param, ke_task_id_t const dest_id, ke_task_id_t const src_id) { uint8_t i; if (param->length > 0) { QPRINTF(“len=%d, I%02X”, param->length, param->data[0]); QPRINTF(“\r\n”); QPRINTF(“the receive data is :”); for(i=0;ilength;i++) { QPRINTF("%x",param->data[i]); } } QPRINTF("\r\n");

     return (KE_MSG_CONSUMED);  }  ```  当发送数据为29时,CuteCom蓝牙接收数据为
    

    QN BLE is ready. Set device name complete Advertising start. Connection with 1FDA7E9F8E30 result is 0x0. LTK request indication idx is 0, auth_req is 0. Start encryption complete, idx 0, status 0, key_size 0, sec_prop 1, bonded 0. Slave update success. Update parameter complete, interval: 0xc, latency: 0x0, sup to: 0x12c. len=1, I29 the receive data is :29

  5. App收取蓝牙通知。通过点击app上5个特征的通知,能获取蓝牙模块的实时通知。Gatt层与通知相关的函数是qpps_data_send_req_handler,但我们只需要修改app开头的api即可完成相应的开发,现象及分析如下: 
    当点击app上的通知时,会进入如下的函数,为了调试将其调整为如下:

    ```c int app_qpps_cfg_indntf_ind_handler(ke_msg_id_t const msgid, struct qpps_cfg_indntf_ind *param, ke_task_id_t const dest_id, ke_task_id_t const src_id) { if (app_qpps_env->conhdl == param->conhdl) { QPRINTF(“enter app_qpps_cfg_indntf_ind_handler”); QPRINTF(“\r\n”); if (param->cfg_val == PRF_CLI_START_NTF) { QPRINTF(“enter PRF_CLI_START_NTF”); QPRINTF(“\r\n”); app_qpps_env->features |= (QPPS_VALUE_NTF_CFG « param->char_index); QPRINTF(“app_qpps_env->features is %d, param->char_index is %d”,app_qpps_env->features,param->char_index); QPRINTF(“\r\n”); // App send data if all of characteristic have been configured if (get_bit_num(app_qpps_env->features) == app_qpps_env->tx_char_num)//num is 5 { QPRINTF(“enter app_qpps_env->features”); QPRINTF(“\r\n”); app_qpps_env->char_status = app_qpps_env->features; app_test_send_data(app_qpps_env->tx_char_num - 1); } } else { app_qpps_env->features &= ~(QPPS_VALUE_NTF_CFG « param->char_index); app_qpps_env->char_status &= ~(QPPS_VALUE_NTF_CFG « param->char_index); } }

     return (KE_MSG_CONSUMED);  }  ```  其中,param->char_index代表第几个可通知特征,app_qpps_env->tx_char_num为特征的数量5,只有当所有特征都进入通知状态时蓝牙模块才会发数据到手机app。     
    

    数据发送的过程见如下函数,这个函数中的数据val可以改为项目中的需求,如IO口状态

    ```c static void app_test_send_data(uint8_t max) { uint8_t cnt;

     QPRINTF("enter app_test_send_data");
     QPRINTF("\r\n");
     for (cnt = 0; (max != 0) && cnt < app_qpps_env->tx_char_num; cnt++)
     {
         if ((app_qpps_env->char_status >> cnt) & QPPS_VALUE_NTF_CFG)
         {
             static uint8_t val[] = {0, '0', '1', '2','3','4','5','6','7','8','9','8','7','6','5','4','3','2','1','0'};
    
             // Increment the first byte for test 
             val[0]++;
    
             max--;
             // Allow next notify until confirmation received in this characteristic
             app_qpps_env->char_status &= ~(QPPS_VALUE_NTF_CFG << cnt);
             app_qpps_data_send(app_qpps_env->conhdl, cnt, sizeof(val), val);
         }
     }  }  ```  数据发送后,有一个发送确认函数app_qpps_data_send_cfm_handler,发送成功后手机上看到的现象是这样的
    

至此完成的功能为,实时反应蓝牙通知信息,app发数据控制蓝牙模块。


####QTool使用 QTool是一个运行在PC端的应用软件,允许用户启动两个BLE设备之间的连接,它能帮助用户分析BLE蓝牙。它是通过串口与BLE设备进行通信,通过ACI命令扮演网络处理器的作用。

打开pdf使用文档

~ $ cd /home/nephne/.wine/drive_c/QBlue/QN9020/QBlue-1.3.7/Documents
~ $ evince QBlue\ ISP\ Studio\ Manual\ v1.0.pdf

插入USB连接蓝牙模块,下载np_controller_B2.bin,然后打开QTool进入设备连接。关于界面说明见系统文档。

例如:点击Setting-Server-QPPS里的Create DB,然后进入Setting-Mode里点击Advertising,即可实现如上QPPS工程的效果。而在旁边的Local Device Traces窗口可以看到程序的大概运行过程。


####参考文档 - FireBLE-wiki - BLE编程API参考手册 - BLE软件开发手册 - 数据手册 - QN9020快速入门 - QN902x周立功版 - 防丢器项目 - MDK-ARM PRO SET - 总有“一道菜”适合你——BLE - 【FireBLE试用体验】体验报告汇总 -FireBLE 开发板试用汇总贴(2015.11.29更新 共收录145篇) [板块置顶] [精华] - FireBLE驱动第一篇:呼吸灯 -FireBLE低功耗蓝牙开发板评测 > 可穿戴手环+蓝牙防丢器 - esp8266

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值