ThreadX(三)------线程thread

常用API

  • tx_thread_create
  • tx_thread_delete
  • tx_thread_preemption_change
  • tx_thread_priority_change
  • tx_thread_relinquish
  • tx_thread_reset
  • tx_thread_resume
  • tx_thread_sleep
  • tx_thread_suspend
  • tx_thread_terminate
  • tx_thread_wait_abort

tx_thread_create

UINT tx_thread_create(TX_THREAD *thread_ptr,
  CHAR *name_ptr, VOID (*entry_function)(ULONG),
  ULONG entry_input, VOID *stack_start,
  ULONG stack_size, UINT priority,
  UINT preempt_threshold, ULONG time_slice,
  UINT auto_start);
  • thread_ptr指向线程控件块的指针。
  • name_ptr指向线程名称的指针。
  • entry_function指定线程执行的初始 C 函数。当线程从此条目函数返回时,它将处于已完成状态并无限期挂起。
  • entry_input线程首次执行时传递给线程的输入函数的 32 位值。此输入的使用完全由应用程序决定。
  • stack_start堆栈内存区域的起始地址。
  • stack_size堆栈内存区域中的字节数。线程的堆栈区域必须足够大,以处理其最坏情况下的函数调用嵌套和本地变量使用。
  • 优先级线程的数字优先级。法律值的范围为 0 到 TX_MAX_PRIORITES-1,其中值 0 表示最高优先级。
  • preempt_threshold禁用抢占的最高优先级 (0 到 TX_MAX_PRIORITIES-1))。只有高于此级别的优先级才能抢占此线程。此值必须小于或等于指定的优先级。等于线程优先级的值禁用抢占阈值。
  • time_slice允许此线程在获得运行机会之前运行此线程的计时器刻度数。请注意,使用抢占阈值可禁用时间切片。法定时间切片值范围为 1 到 0xFFFF(含)。值TX_NO_TIME_SLICE(值为 0)将禁用此线程的时间切片。
  • auto_start指定线程是立即启动还是处于挂起状态。法律选项包括TX_AUTO_START (0x01) 和TX_DONT_START (0x00)。如果TX_DONT_START,应用程序稍后必须调用tx_thread_resume才能运行线程。

返回值

  • TX_SUCCESS (0x00) 成功创建线程。
  • TX_THREAD_ERROR (0x0E) 无效线程控制指针。指针为 NULL 或线程已创建。
  • TX_PTR_ERROR (0x03) 入口点或堆栈区域的无效起始地址无效,通常为 NULL。
  • TX_SIZE_ERROR (0x05) 堆栈区域的大小无效。线程必须至少具有TX_MINIMUM_STACK字节。
  • TX_PRIORITY_ERROR (0x0F) 无效线程优先级,这是超出范围 (0 到 (TX_MAX_PRIORITIES-1) 的值。
  • TX_THRESH_ERROR (0x18) 无效抢占指定。此值必须是小于或等于线程的初始优先级的有效优先级。
  • TX_START_ERROR (0x10) 自动启动选择无效。
  • TX_CALLER_ERROR服务(0x13)无效调用。

tx_thread_delete

UINT tx_thread_delete(TX_THREAD *thread_ptr);
  • thread_ptr指向以前创建的应用程序线程的指针。

返回值

  • TX_SUCCESS (0x00) 成功删除线程。
  • TX_THREAD_ERROR (0x0E) 无效的应用程序线程指针。
  • TX_DELETE_ERROR (0x11) 指定的线程未处于终止或已完成状态。
  • TX_CALLER_ERROR服务(0x13)无效调用。

tx_thread_preemption_change

UINT tx_thread_preemption_change(TX_THREAD *thread_ptr,
    UINT new_threshold, UINT *old_threshold);
  • thread_ptr指向以前创建的应用程序线程的指针。
  • new_threshold新的抢占阈值优先级(0 到 (TX_MAX_PRIORITIES-1)。
  • old_threshold指向位置的指针以返回上一个抢占阈值。

返回值

  • TX_SUCCESS (0x00) 成功抢占阈值更改。
  • TX_THREAD_ERROR (0x0E) 无效的应用程序线程指针。
  • TX_THRESH_ERROR (0x18) 指定的新抢占阈值不是有效的线程优先级(0 到 (TX_MAX_PRIORITIES-1))以外的值,或大于(优先级较低)的当前线程优先级。
  • TX_PTR_ERROR (0x03) 无效指针指向以前的抢占保留存储位置。
  • TX_CALLER_ERROR服务(0x13)无效调用方。

tx_thread_priority_change

UINT tx_thread_priority_change(TX_THREAD *thread_ptr,
    UINT new_priority, UINT *old_priority);
  • thread_ptr指向以前创建的应用程序线程的指针。
  • new_priority新线程优先级(0 到 (TX_MAX_PRIORITIES-1)。
  • old_priority指向位置的指针以返回线程的上一个优先级。

返回值

  • TX_SUCCESS (0x00) 成功优先级更改。
  • TX_THREAD_ERROR (0x0E) 无效的应用程序线程指针。
  • TX_PRIORITY_ERROR (0x0F) 指定的新优先级无效(0 到TX_MAX_PRIORITIES-1) 以外的值)。
  • TX_PTR_ERROR (0x03) 指向上一个优先级存储位置的无效指针。
  • TX_CALLER_ERROR服务(0x13)无效调用。

tx_thread_relinquish

VOID tx_thread_relinquish(VOID);

以相同或更高的优先级将处理器控制放弃给其他随时可以运行的线程。

tx_thread_reset

UINT tx_thread_reset(TX_THREAD *thread_ptr);
  • thread_ptr指向以前创建的线程的指针。

返回值

  • TX_SUCCESS (0x00) 成功重置线程。

  • TX_NOT_DONE (0x20) 指定的线程未处于TX_COMPLETED或者TX_TERMINATED状态。

  • TX_THREAD_ERROR (0x0E) 无效线程指针。

  • TX_CALLER_ERROR服务(0x13)无效调用。

tx_thread_resume

UINT tx_thread_resume(TX_THREAD *thread_ptr);
  • thread_ptr指向挂起的应用程序线程的指针。

返回值

  • TX_SUCCESS (0x00) 成功线程恢复。
  • TX_SUSPEND_LIFTED (0x19) 先前设置的延迟悬挂已解除。
  • TX_THREAD_ERROR (0x0E) 无效的应用程序线程指针。
  • TX_RESUME_ERROR (0x12) 指定的线程未挂起,或以前由服务挂起,tx_thread_suspend。

tx_thread_sleep

UINT tx_thread_sleep(ULONG timer_ticks);
  • timer_ticks挂起调用应用程序线程的计时器数,范围从 0 到 0xFFFF。如果指定了 0,服务将立即返回。

返回值

  • TX_SUCCESS (0x00) 成功的线程睡眠。
  • TX_WAIT_ABORTED (0x1A) 挂起被另一个线程、计时器或 ISR 中止。
  • TX_CALLER_ERROR (0x13) 服务从非线程调用。

tx_thread_suspend

UINT tx_thread_suspend(TX_THREAD *thread_ptr);
  • thread_ptr指向应用程序线程的指针。

返回值

  • TX_SUCCESS (0x00) 成功线程挂起。
  • TX_THREAD_ERROR (0x0E) 无效的应用程序线程指针。
  • TX_SUSPEND_ERROR (0x14) 指定的线程处于终止或已完成状态。
  • TX_CALLER_ERROR服务(0x13)无效调用。

tx_thread_terminate

UINT tx_thread_terminate(TX_THREAD *thread_ptr);
  • thread_ptr指向应用程序线程的指针。

返回值

  • TX_SUCCESS (0x00) 成功线程终止。
  • TX_THREAD_ERROR (0x0E) 无效的应用程序线程指针。
  • TX_CALLER_ERROR服务(0x13)无效调用方。

tx_thread_wait_abort

UINT tx_thread_time_slice_change(TX_THREAD *thread_ptr,
    ULONG new_time_slice, ULONG *old_time_slice);
  • thread_ptr指向以前创建的应用程序线程的指针。

返回值

  • TX_SUCCESS (0x00) 成功线程等待中止。
  • TX_THREAD_ERROR (0x0E) 无效的应用程序线程指针。
  • TX_WAIT_ABORT_ERROR (0x1B) 指定的线程未处于等待状态。

threadx_demo

/*
 *
 * SPDX-License-Identifier: GPL-2.0-or-later
 *
 * Change Logs:
 * Date           Author       Notes
 * 2020-08-15    Psycho_real   the first version
 */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "spi.h"
#include "tim.h"
#include "usart.h"
#include "gpio.h"

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "tx_api.h"
/* USER CODE END Includes */
	......
/* USER CODE BEGIN PV */
TX_THREAD my_thread_1;
TX_THREAD my_thread_2;
uint8_t pData[] = "=========ThreadX=========\n";
uint8_t pData1[] = "I am thread1 ";
uint8_t pData2[] = "I am thread2 ";
/* USER CODE END PV */
	......	
/**
 * @brief  The application entry point.
 * @retval int
 */
int main(void)
{
	......
	
	/* USER CODE BEGIN 2 */

	tx_kernel_enter(); //threadx 入口

	/* USER CODE END 2 */
	/* Infinite loop */
	/* USER CODE BEGIN WHILE */
	while (1);
	/* USER CODE END 3 */
}

/* USER CODE BEGIN 4 */
void thread1_entry(ULONG entry_input)
{

	INT count = 0;
	uint8_t init_data[]="start now";
	while (1)
	{

		HAL_UART_Transmit(&huart1, pData1, sizeof(pData1), HAL_MAX_DELAY);
		if(count == 0)
		{
			HAL_UART_Transmit(&huart1, init_data, sizeof(init_data), HAL_MAX_DELAY);
		}
		count++;
		//HAL_GPIO_TogglePin(GPIOE, GPIO_PIN_7|GPIO_PIN_8);

		tx_thread_sleep(1000); // 线程睡眠1000 timer_ticks

	}
}

void thread2_entry(ULONG entry_input)
{
	INT count = 0;
	while (1)
	{
		HAL_UART_Transmit(&huart1, pData2, sizeof(pData2), HAL_MAX_DELAY);
		if (count == 3)
		{
			/*挂起线程1*/
			tx_thread_suspend(&my_thread_1);
		}
		else if (count == 6)
		{
			/*恢复线程1*/
			tx_thread_resume(&my_thread_1);
		}
		else if (count == 9)
		{
			/*终止线程1*/
			tx_thread_terminate(&my_thread_1);
		}
		else if (count == 12)
		{
			/*重置线程1*/
			tx_thread_reset(&my_thread_1);
			/*恢复线程1*/
			tx_thread_resume(&my_thread_1);
		}
		else if (count == 13)
		{
			/*终止线程1-2*/
			tx_thread_terminate(&my_thread_1);
			tx_thread_terminate(&my_thread_2);
		}
		else
		{
			;
		}
		count++;
		tx_thread_sleep(1000); // 线程睡眠500 timer_ticks
	}
}
void my_entry_exit_notify(TX_THREAD *thread_ptr, UINT condition)
{
	uint8_t entry_data[] = " thread1-entry ";
	uint8_t exit_data[] = " thread1-exit ";
	/* Determine if the thread was entered or exited. */

	if (condition == TX_THREAD_ENTRY)
	{
		/* Thread entry! */
		HAL_UART_Transmit(&huart1, entry_data, sizeof(pData2), HAL_MAX_DELAY);
		//HAL_GPIO_TogglePin(GPIOE, GPIO_PIN_7|GPIO_PIN_8|GPIO_PIN_9);
	}
	if (condition == TX_THREAD_EXIT)
	{
		/* Thread exit! */
		HAL_UART_Transmit(&huart1, exit_data, sizeof(pData2), HAL_MAX_DELAY);
	}

}

void tx_application_define(void *first_unused_memory)
{

	/*线程1*/
	tx_thread_create(&my_thread_1,	//线程控制块指针
			"my_thread1",//线程名字
			thread1_entry,//线程入口函数
			0,//线程入口参数
			first_unused_memory,//线程的起始地址(这里偷懒,没有进行分配,直接使用未用的起始地址)
			1024,//内存区域大小K
			3,//优先级3  (0~TX_MAX_PRIORITES-1)0  表示最高优先级
			3,//禁用抢占的最高优先级
			TX_NO_TIME_SLICE,//时间切片值范围为 1 ~ 0xFFFF(TX_NO_TIME_SLICE = 0)
			TX_AUTO_START//线程自动启动
			);
	/*线程2*/
	tx_thread_create(&my_thread_2,	//线程控制块指针
			"my_thread2",//线程名字
			thread2_entry,//线程入口函数
			0,//线程入口参数
			first_unused_memory+1024,//线程的起始地址+1024 (-被前面线程用掉了)
			1024,//内存区域大小K
			1,//优先级3  (0~TX_MAX_PRIORITES-1)0  表示最高优先级
			1,//禁用抢占的最高优先级
			TX_NO_TIME_SLICE,//时间切片值范围为 1 ~ 0xFFFF(TX_NO_TIME_SLICE = 0)
			TX_AUTO_START//线程自动启动
			);
	/*线程进入和退出时通知*/
	tx_thread_entry_exit_notify(&my_thread_1, my_entry_exit_notify);
}
/* USER CODE END 4 */

	......

在这里插入图片描述

  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
【RT-Thread作品秀】智慧酒店新风监控系统作者:焦玉雷 (注:具体图片见“其他文件”的附件文档) 概述1、应用背景绝大多数客房由于长期密闭,往往无法做到良好的通风换气。室内异味,潮气,烟味等污染物聚集在室内,客户的睡眠质量也会因缺乏充足新鲜洁净空气而受影响。智慧新风系统通过不断送入过滤好的外界新鲜空气解决这些问题,提高客户的满意度,回访率和推荐频率。为保证客户在房内对新鲜空气的需求,高档酒店常装有新风系统,但现有新风系统存在正负压差不稳定、运维成本高等问题。智慧新风系统通过调整空气压力,监控风机状态,并将系统信息基于MQTT协议传输至中控系统,实现边缘敏捷互联,提高酒店客户的满意度,回访率和推荐频率。 2、功能介绍智慧酒店新风监控系统能够实现酒店的气压检测,实时调整新风系统的输入输出,为酒店客户提供健康舒适的呼吸环境;同时监控系统对风机的振动状态进行实时监控,可以实现对风机状态进行感知预测,提早发现风机的异常故障状态。 开发环境硬件:ART-Pi、六轴传感器(LSM6DSOX)、气压传感器(LPS22HH、WIFI模块(USR-WIFI232-D2) RT-Thread版本:3.1.3 开发工具及版本:keilV5.31.0.0 VS2017 RT-Thread使用情况概述目前,使用了RT-Thread内核、多线程、信号量、事件集。 RT-Thread操作系统是一款基于优先级和时间片轮转的多任务实时操作系统。优先级其实是给任务分配的一个数值,数值越小则优先级越高。优先级的高低将直接反应在任务调度算法中,优先级越高越优先响应。RT-Thread对任务的调度是基于时间片的轮转,时间片轮转调度会在每个TICK中断时对当前任务的时间片减一,然后检查其它任务的时间片剩余情况。一旦当前任务的时间片用完,则会先重置当前任务的时间片。然后看是否有相同优先级的任务,如果有则会将当前任务移到队列末尾。然后触发优先级调度,此时只要当前优先级是已就绪的最高优先级最终就会取出相同优先级队列头的任务运行。 硬件框架边缘端硬件使用比赛提供的STM32H750开发板,采集酒店的压力和风机的振动信息,判定酒店环境和风机硬件状态,通过WIFI连接网络,将数据通过MQTT协议发送至云平台。结合六轴传感器(LSM6DSOX)和气压传感器将酒店新风系统中的气压,室内温度,风机振动信息上传至云端,在云端可以实时获取新风系统状态信息,并可以基于通信协议对风机进行控制。 图片无法上传,具体图片见“其他文件”的附件文档 软件框架说明智慧酒店新风监控系统中网络层适用MQTT传输消息,MQTT是一个基于客户端-服务器的消息发布/订阅传输协议,轻量、简单、开放和易于实现的,这些特点使它适用范围非常广泛,尤其适用物联网(IoT)场景。 STM32H750开发板使用CUBEMX配置硬件接口,自动生成HAL库文件,再基于该软件移植RT-Thread操作系统,移植完操作系统可以进行应用层的代码编写。 软件PC界面适用C#在WPF框架下进行编写,WPF分离界面设计人员与开发人员的工作,界面效果适用XAML语言进行表示,后台功能使用C#进行快捷开发。 软件界面如下图所示:图片无法上传,具体图片见“其他文件”的附件文档 PC监控系统软件示意图:图片无法上传,具体图片见“其他文件”的附件文档 服务器使用华为云服务器,在华为云上使用emqtt的docker快速搭建MQTT服务器,边缘端向topic为/hotel/pressure发布消息,消息中报含气压、振动特征和单片机跟据状态判定的新风系统状态。 软件模块说明智慧酒店新风监控系统硬件系统包含数据采集模块、通信协议模块、MQTT数据通讯模块、新风系统控制模块四部分组成。 数据采集模块采集振动信息、气压信息和温度信息。 通信协议模块将采集到的信息基于自定义的通信协议制作成通信帧。 MQTT数据通讯模块将数据打包为MQTT协议帧,通过连接MQTT服务器,订阅MQTT-topic消息,发布MQTT消息将数据上传。 新风系统控制模块集成了边缘计算的功能,通过对风机振动信息的检测,或者云端的控制信息,实现本地对新风系统的控制功能。 演示效果硬件连接图图片无法上传,具体图片见“其他文件”的附件文档 下位机软件架构图图片无法上传,具体图片见“其他文件”的附件文档 上位机展示图图片无法上传,具体图片见“其他文件”的附件文档 视频: 比赛感悟首先,非常感谢RT-Thread x STM32 全连接创意创客这次比赛! 通过这次比赛了解到了RT-Thread作为一个有十余年历史的国产RTOS,性能上面不输于ucos和FreeRTOS等操作系统,移植性上也非常方便,为硬件工作人员减轻了开发难度。 我学习到了RT-Thread的开源精神,将自己业余时间的代码开源,积极
概述数采一期下位机是基于ART-PI开发板开发,服务器采用阿里云学生版云服务器ECS,单机器部署支持每秒采集2000台设备温度压力数据,连接对象(下称下位机)可以是设备也可以是网关(连接协议目前仅支持基于WebSocket,详情参考SocketIO),下位机采集或汇总的数据并通过 WIFI 上传云端远程实时监视,也可本地连接串口与 PC 端通讯,上位机通过自定义解析数据后展示到相关订阅端(可同时同步到多个Android手机)。对于物联网初创公司、自由开发者、学生,是一个相当实用的设计。数据流向完全透明,不存在黑匣子,有助于开发者快速掌握物联网运行流程。 开发环境硬件:ART-PI RT-Thread版本:RT-Thread Nano v3.1.3 开发工具及版本:CubeMX v6.1.0、Keil v5.33、VSCode v1.51.1、Android Studio 4.1.1、HBuilder X v2.9.8.20201110、NodeJS v14.15.1、MongoDB v4.4.1(1主2副)、redis v6.0 RT-Thread使用情况概述内核部分:调度器。 软件包:CJSON v1.7.7 硬件框架ART-Pi是 RT-Thread 团队经过半年的精心准备,专门为嵌入式软件工程师、开源创客设计的一款极具扩展功能的 DIY 开源硬件。 软件框架说明本项目采用WebSocket协议进行设备与云端互联,本期目标,融合CubeMX、keil、RTT Nano的同时提供OTA功能和压力传感器(LPS22HH)的示例。本项目可分为4个部分: 第一部分 服务端 本期采用关键技术有nodejs、eggjs、socketio、mongodb等,主要为设备提供云端连接服务、可定制化协议解析、消息转发,报警等功能。 第二部分 后台管理端 本期采用关键技术有vue、element-ui、vue-router、vuex、monaco-editor等,主要提供一些权限分组管理,设备模型的定义(不同协议的解析),设备OTA测试。 第部分 下位机 本期采用关键技术有RTT Nano、CJson、WebSocketClient等,主要实现了设备温度采集上传,模拟温度过载报警,OTA,以及C++14的示例代码。 第四部分 移动端 本期采用关键技术有uniapp、vue、echarts等,主要实现了设备数据实时上报、数据下发、模拟报警的功能。 软件模块说明sensor_thread_entry:传感器线程,采集数据并通过WebSocketClient发送给云端。 led_ thread _entry:闪灯线程,用来指示当前系统的运行状态。 wifi_ thread _entry:wifi线程,用来处理来自服务器的数据。 演示效果视频: 代码地址请下载附件获取代码。 若使用上有啥问题,请联系QQ: 296565890,微信:xiaosichuan2013 本项目遵循 Apache 许可证 2.0 版本,所包含4个端均可以免费在商业产品中使用,不需要公布应用程序源码,没有潜在商业风险。 真心希望有志同道合的朋友一期参与此开源项目 工作时间不一定及时回复,忘谅解! 比赛感悟纸上得来终觉浅,绝知此事要躬行。陆游的这首诗,完美的诠释了我在这次比赛中的收获。 物联网的时代,RTOS多线程是核心,通过综合对比,我最终选择了国产的RT-Thread。 通过本次比赛,让我深深体会到RTT Nano的小而美,尤其是在改动部分源码后,RTT Nano支持C++ 14,lambda、auto、函数重载、类模板用起来真是爽!(开启C++支持) 虽然本次比赛没有用到RT-Thread提供的软件包,但手动写一次WebSocketClient收获已经很满意了。 最后感谢主办方提供了这么好的一个平台,不仅能展示自我,也能学到很多知识,还要感谢论坛上那些解决我问题以及提供技术支持的大佬,希望有朝一日我也能给开源社区贡献一份自己力量。
以下是使用 Python 代码实现的基于 Miller-Rabin 算法的多线程判断素数的示例: ```python import threading import math # 判断是否为素数 def is_prime(n, k): if n <= 1 or (n > 2 and n % 2 == 0): return False d = n - 1 while d % 2 == 0: if pow(k, d // 2, n) == n - 1: return True d //= 2 x = pow(k, d, n) return x == 1 or x == n - 1 # 多线程判断素数 def parallel_is_prime(n, k, start, end, results, lock): for i in range(start, end + 1): if not is_prime(i, k): with lock: results[i] = False return with lock: for i in range(start, end + 1): results[i] = True # 主函数 if __name__ == "__main__": n = 1000000 # 需要判断的数 k = 2 # 判断素数的基数 num_threads = 4 # 线程数 results = [False] * (n + 1) # 判断结果 lock = threading.Lock() # 互斥锁 threads = [] # 线程数组 block_size = n // num_threads # 每个线程处理的块大小 for i in range(num_threads): start = i * block_size + 1 end = n if i == num_threads - 1 else (i + 1) * block_size t = threading.Thread(target=parallel_is_prime, args=(n, k, start, end, results, lock)) threads.append(t) for t in threads: t.start() for t in threads: t.join() is_all_prime = all(results[1:]) # 判断所有数是否都是素数 if is_all_prime: print(n, "is a prime number.") else: print(n, "is not a prime number.") ``` 在这个示例代码中,我们首先定义了一个 `is_prime` 函数来判断一个数是否为素数,该函数采用 Miller-Rabin 算法进行判断。接下来,我们定义了一个 `parallel_is_prime` 函数,用于在一个线程内判断一段范围内的数是否为素数。这个函数的参数包括需要判断的数 n、判断素数的基数 k、该线程处理的起始位置和结束位置、判断结果 results 和互斥锁 lock。在函数内部,我们首先对该范围内的每个数进行判断,如果发现某个数不是素数,则可以直接将该数的判断结果设置为 False,并退出该线程的运行。如果该范围内的所有数都是素数,则将这些数的判断结果设置为 True。最后,我们在主函数中创建多个线程来并发地判断素数,并在所有线程运行完毕后,将结果进行合并。如果所有数都是素数,则说明该数是素数,否则,该数为合数。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值