linux下之定时器timer实例

最近工作中涉及到在一定的时间之后对特定的动作进行处理。比如按键,按键需要在按下至少4S之后,重新启动系统之类、此时使用定时器,可以确保准确的计时。
下面的实现可以通用。
struct timer_list os_timer_t; //定义定时器

#define OS_TIMER_FUNC(_fn)	\
	void _fn(unsigned long timer_arg)

#define OS_GET_TIMER_ARG(_arg, _type)	\
	(_arg) = (_type)(timer_arg)

#define OS_INIT_TIMER(_osdev, _timer, _fn, _arg)	\
do {							\
	init_timer(_timer);				\
	(_timer)->function = (_fn);			\
	(_timer)->data = (unsigned long)(_arg);		\
} while (0)

#define OS_SET_TIMER(_timer, _ms)	\
	mod_timer(_timer, jiffies + ((_ms)*HZ)/1000)

#define OS_CANCEL_TIMER(_timer)		del_timer(_timer)


1:初始化定时器
		//初始化,其处理函数为:wps_led_blink
		OS_INIT_TIMER(NULL, &os_timer_t, wps_led_blink, &os_timer_t); 
2:启动定时器/设置定时器
		OS_SET_TIMER(&os_timer_t, 1000);//设置表示,在1000ms之后启动定时器。

3:取消定时器:
	 OS_CANCEL_TIMER(&os_timer_t);


4:定时器的处理函数定义如下:
static OS_TIMER_FUNC(wps_led_blink)
{
   static int WPSled = WPS_LED_ON,sec = 0;
   ar7242_gpio_out_val(WPS_LED_GPIO,WPSled);
   WPSled=!WPSled;  
   OS_SET_TIMER(&os_timer_t, 1000);
   /*
   sec++ ;
   if(sec < 130) 
   {
		OS_SET_TIMER(&os_timer_t, 1000);
   }
   else 
   {
		sec = 0;
		wps_led_blinking = 0 ;
		OS_CANCEL_TIMER(&os_timer_t);
		ar7242_gpio_out_val (WPS_LED_GPIO, WPS_LED_OFF);	
   }*/
}
上面的处理函数可知:每隔1000ms即1S对WPS_LED_GPIO输出高低电平。使其周期性的闪烁。

完整的测试代码如下:
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/signal.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/init.h>
#include <linux/resource.h>
#include <linux/proc_fs.h>
#include <linux/miscdevice.h>
#include <asm/types.h>
#include <asm/irq.h>
#include <asm/delay.h>
#include <asm/system.h>
//Ar7240.h (e:\sdk_wlan_db12x\sdk\wlan-ap\linux\kernels\mips-linux-2.6.31\arch\mips\include\asm\mach-ar7240)	40773	2011-12-1

#include <asm/mach-ar7240/ar7240.h>

#include <asm/mach-atheros/724x.h>
#define WPS_LED_GPIO 0
#define WPS_LED_OFF 1
#define WPS_LED_ON  0
#define SIMPLE_CONFIG_OFF     1
#define SIMPLE_CONFIG_ON      2     
#define SIMPLE_CONFIG_BLINK   3

typedef enum {
        LED_STATE_OFF   =       1,
        LED_STATE_ON    =       2,
        LED_STATE_BLINKING =    3,
} led_state_e;

#define OS_TIMER_FUNC(_fn)                      \
    void _fn(unsigned long timer_arg)

#define OS_GET_TIMER_ARG(_arg, _type)           \
    (_arg) = (_type)(timer_arg)

#define OS_INIT_TIMER(_osdev, _timer, _fn, _arg) \
do {                                             \
        init_timer(_timer);                      \
        (_timer)->function = (_fn);              \
        (_timer)->data = (unsigned long)(_arg);  \
} while (0)

#define OS_SET_TIMER(_timer, _ms)       mod_timer(_timer, jiffies + ((_ms)*HZ)/1000)

#define OS_CANCEL_TIMER(_timer)         del_timer(_timer)
static struct proc_dir_entry *simple_config_entry = NULL;
static struct proc_dir_entry *simple_config_led_entry = NULL;
static int wps_led_blinking = 0;
struct timer_list  os_timer_t;
static led_state_e simple_config_led_state = LED_STATE_BLINKING;

void ar7242_gpio_config_output(int gpio)
{
#ifdef CONFIG_WASP_SUPPORT
    ar7240_reg_rmw_clear(AR7240_GPIO_OE, (1 << gpio));
#else
    ar7240_reg_rmw_set(AR7240_GPIO_OE, (1 << gpio));
#endif
}

void ar7242_gpio_config_input(int gpio)
{
#ifdef CONFIG_WASP_SUPPORT
    ar7240_reg_rmw_set(AR7240_GPIO_OE, (1 << gpio));
#else
    ar7240_reg_rmw_clear(AR7240_GPIO_OE, (1 << gpio));
#endif
}

void ar7242_gpio_out_val(int gpio, int val)
{
    if (val & 0x1) {
        ar7240_reg_rmw_set(AR7240_GPIO_OUT, (1 << gpio));
    }
    else {
        ar7240_reg_rmw_clear(AR7240_GPIO_OUT, (1 << gpio));
    }
}
int ar7242_gpio_in_val(int gpio)
{
    return((1 << gpio) & (ar7240_reg_rd(AR7240_GPIO_IN)));
}

static OS_TIMER_FUNC(wps_led_blink)
{
   static int WPSled = WPS_LED_ON,sec = 0;
   ar7242_gpio_out_val(WPS_LED_GPIO,WPSled);
   WPSled=!WPSled;  
   OS_SET_TIMER(&os_timer_t, 1000);
   /*
   sec++ ;
   if(sec < 130) 
   {
		OS_SET_TIMER(&os_timer_t, 1000);
   }
   else 
   {
		sec = 0;
		wps_led_blinking = 0 ;
		OS_CANCEL_TIMER(&os_timer_t);
		ar7242_gpio_out_val (WPS_LED_GPIO, WPS_LED_OFF);	
   }*/
}
static int gpio_simple_config_led_read (char *page, char **start, off_t off,
               int count, int *eof, void *data)
{
    return sprintf (page, "%d\n", simple_config_led_state);
}

static int gpio_simple_config_led_write (struct file *file, const char *buf,unsigned long count, void *data)
{
	u_int32_t val = 0;
  if(sscanf(buf, "%d", &val) != 1)
	{
		return -EINVAL;
	}
	if ((val == SIMPLE_CONFIG_BLINK) && !wps_led_blinking)  /* wps LED blinking */
	{
		wps_led_blinking = 1 ;
		simple_config_led_state = SIMPLE_CONFIG_BLINK ;
		ar7242_gpio_out_val (WPS_LED_GPIO, WPS_LED_ON);	
	//	OS_INIT_TIMER(NULL, &os_timer_t, wps_led_blink, &os_timer_t);
		OS_SET_TIMER(&os_timer_t, 1000);
	}
	else if (val == SIMPLE_CONFIG_ON) /* WPS Success  */
	{
		wps_led_blinking = 0 ;
		simple_config_led_state = SIMPLE_CONFIG_ON ;
		OS_CANCEL_TIMER(&os_timer_t);
		ar7242_gpio_out_val (WPS_LED_GPIO, WPS_LED_ON);
	}
	else if (val == SIMPLE_CONFIG_OFF)  /* WPS failed */
	{
		wps_led_blinking = 0 ;
		simple_config_led_state = SIMPLE_CONFIG_OFF ;
		OS_CANCEL_TIMER(&os_timer_t);
		ar7242_gpio_out_val (WPS_LED_GPIO, WPS_LED_OFF);
	} 

  return count;
}
static int create_simple_config_led_proc_entry (void)
{
    if (simple_config_entry != NULL) {
        printk ("Already have a proc entry for /proc/simple_config!\n");
        return -ENOENT;
    }
    simple_config_entry = proc_mkdir("simple_config", NULL);
    if (!simple_config_entry)
        return -ENOENT;
    simple_config_led_entry = create_proc_entry ("simple_config_led", 0644,
                                            simple_config_entry);
    if (!simple_config_led_entry)
        return -ENOENT;

    simple_config_led_entry->write_proc = gpio_simple_config_led_write;
    simple_config_led_entry->read_proc = gpio_simple_config_led_read;

    /* configure gpio as outputs */
    ar7242_gpio_config_output (WPS_LED_GPIO); 

    /* switch off the led */
    ar7242_gpio_out_val(WPS_LED_GPIO, WPS_LED_OFF);
    OS_INIT_TIMER(NULL, &os_timer_t, wps_led_blink, &os_timer_t);
		OS_SET_TIMER(&os_timer_t, 1000);//此处启动定时器。
    return 0;
}


//ar7240_reg_rd(AR7240_GPIO_IN_ETH_SWITCH_LED)=480fa 
//ar7240_reg_rd(AR7240_GPIO_IN_ETH_SWITCH_LED)=4800a 
static int __init init_ar7242_gpio_module(void)
{
	unsigned int rddata;
  rddata = ar7240_reg_rd(AR7240_GPIO_IN_ETH_SWITCH_LED);
  printk("first:reg 0x18040028=%x\n",rddata);   
  rddata = rddata & (~(0xf0));    
  ar7240_reg_wr(AR7240_GPIO_IN_ETH_SWITCH_LED, rddata);  	
  rddata = ar7240_reg_rd(AR7240_GPIO_IN_ETH_SWITCH_LED);
  printk("second:reg 0x18040028=%x\n",rddata);   
	create_simple_config_led_proc_entry();
	printk("***  init_ar7242_gpio_module success *** \n");
	return 0;
}
static void __exit cleanup_ar7242_gpio_module(void)
{			
	  printk("%s (%s) line: %d\n", __FILE__, __func__,__LINE__);			
}
module_init(init_ar7242_gpio_module);
module_exit(cleanup_ar7242_gpio_module);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("suiyuan from test"); 
MODULE_DESCRIPTION("Led driver for Atheros 7242  platform");

--------------------------测试代码二----------------------------------------
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/signal.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/init.h>
#include <linux/resource.h>
#include <linux/proc_fs.h>
#include <linux/miscdevice.h>\
#include <linux/delay.h>
#include <linux/types.h>
#include <asm/types.h>
#include <asm/irq.h>
#include <asm/delay.h>
#include <asm/system.h>


#include <asm/mach-atheros/724x.h>
#include <asm/mach-ar7240/ar7240.h>

#define ATH_FACTORY_RESET		0x89ABCDEF
#define BUTTON_GPIO 11
#define UDELAY_COUNT 8000000
#define BUTTON_MINOR 189
//#define ATH_WDT_TEST_CODE

#ifdef ATH_WDT_TEST_CODE
#define btdbg printk
#else
#define btdbg(junk, ...)
#endif /* ATH_WDT_TEST_CODE 8 */


typedef enum {
    INT_TYPE_EDGE,
    INT_TYPE_LEVEL,
}ar7240_gpio_int_type_t;

typedef enum {
    INT_POL_ACTIVE_LOW,
    INT_POL_ACTIVE_HIGH,
}ar7240_gpio_int_pol_t;

typedef struct   
{
	wait_queue_head_t buttons_waitqueue; /* 等待队列,当没有按键被按下时,如果有进程调用s3c24xx_buttons_read函数,它将休眠 */
	volatile int ev_press;               /* 中断事件标志, 中断服务程序将它置1,s3c24xx_buttons_read将它清0 */
	volatile unsigned int press_cnt;	 /* 4个按键被按下的次数(准确地说,是发生中断的次数) */
	struct timer_list button_timers;     /* buttons delay timer */
	int ar7242_ButtonOpened;
	int button_short_time;
	int is_interrupt_finished;
} buttons_dev_t;
/*
extern void ar7240_gpio_config_int(int gpio, ar7240_gpio_int_type_t type,ar7240_gpio_int_pol_t polarity);
extern void ar7240_gpio_config_output(int gpio);
extern void ar7240_gpio_config_input(int gpio);
extern void ar7240_gpio_out_val(int gpio, int val);
extern int ar7240_gpio_in_val(int gpio);
*/

void ar7240_gpio_config_int(int gpio, ar7240_gpio_int_type_t type,ar7240_gpio_int_pol_t polarity)
{
    u32 val;

    /*
     * allow edge sensitive/rising edge too
     */
    if (type == INT_TYPE_LEVEL) {
        /* level sensitive */
        ar7240_reg_rmw_set(AR7240_GPIO_INT_TYPE, (1 << gpio));
    }
    else {
       /* edge triggered */
       val = ar7240_reg_rd(AR7240_GPIO_INT_TYPE);
       val &= ~(1 << gpio);
       ar7240_reg_wr(AR7240_GPIO_INT_TYPE, val);
    }

    if (polarity == INT_POL_ACTIVE_HIGH) {
        ar7240_reg_rmw_set (AR7240_GPIO_INT_POLARITY, (1 << gpio));
    }
    else {
       val = ar7240_reg_rd(AR7240_GPIO_INT_POLARITY);
       val &= ~(1 << gpio);
       ar7240_reg_wr(AR7240_GPIO_INT_POLARITY, val);
    }

    ar7240_reg_rmw_set(AR7240_GPIO_INT_ENABLE, (1 << gpio));
}

void
ar7240_gpio_config_output(int gpio)
{
#ifdef CONFIG_WASP_SUPPORT
    ar7240_reg_rmw_clear(AR7240_GPIO_OE, (1 << gpio));
#else
    ar7240_reg_rmw_set(AR7240_GPIO_OE, (1 << gpio));
#endif
}

void
ar7240_gpio_config_input(int gpio)
{
#ifdef CONFIG_WASP_SUPPORT
    ar7240_reg_rmw_set(AR7240_GPIO_OE, (1 << gpio));
#else
    ar7240_reg_rmw_clear(AR7240_GPIO_OE, (1 << gpio));
#endif
}

void
ar7240_gpio_out_val(int gpio, int val)
{
    if (val & 0x1) {
        ar7240_reg_rmw_set(AR7240_GPIO_OUT, (1 << gpio));
    }
    else {
        ar7240_reg_rmw_clear(AR7240_GPIO_OUT, (1 << gpio));
    }
}

int
ar7240_gpio_in_val(int gpio)
{
    return((1 << gpio) & (ar7240_reg_rd(AR7240_GPIO_IN)));
}
static  buttons_dev_t buttons_dev =
{
	.ev_press   = 0,
	.press_cnt  = 0,
	.button_short_time =0, 
	.is_interrupt_finished=1,
};
struct timer_list os_timer_t;

#define OS_TIMER_FUNC(_fn)	\
	void _fn(unsigned long timer_arg)

#define OS_GET_TIMER_ARG(_arg, _type)	\
	(_arg) = (_type)(timer_arg)

#define OS_INIT_TIMER(_osdev, _timer, _fn, _arg)	\
do {							\
	init_timer(_timer);				\
	(_timer)->function = (_fn);			\
	(_timer)->data = (unsigned long)(_arg);		\
} while (0)

#define OS_SET_TIMER(_timer, _ms)	\
	mod_timer(_timer, jiffies + ((_ms)*HZ)/1000)

#define OS_CANCEL_TIMER(_timer)		del_timer(_timer)

static OS_TIMER_FUNC(wps_led_blink)
{
	static int sec = 0;
	sec++;
	if(sec < 40) 
	{
	//	printk("sec:%d\n",sec);
		{
			if(ar7240_gpio_in_val(BUTTON_GPIO)==0) 
			{
				OS_SET_TIMER(&os_timer_t, 100);
			  //printk("01_ar7240_gpio_in_val(BUTTON_GPIO):%d\n",ar7240_gpio_in_val(BUTTON_GPIO));
			}
			else
			{
				buttons_dev.button_short_time=0;
				OS_CANCEL_TIMER(&os_timer_t);
				buttons_dev.is_interrupt_finished =1;
				printk("按键时间少于4秒,您按键已:%d 秒\n",(sec/10));	
				sec =0;			
				enable_irq(AR7240_GPIO_IRQn(BUTTON_GPIO));
				//printk("02_ar7240_gpio_in_val(BUTTON_GPIO):%d\n",ar7240_gpio_in_val(BUTTON_GPIO));
	
			}		
		}	
	} 
	else
	{	
		OS_SET_TIMER(&os_timer_t, 100);
		if(ar7240_gpio_in_val(BUTTON_GPIO))
		{
			printk("按键时间大于4秒,您按键已:%d 秒,系统设置将复位。\n",(sec/10));	
			OS_CANCEL_TIMER(&os_timer_t);
			sec =0;		
			buttons_dev.button_short_time=1;
		  buttons_dev.is_interrupt_finished=1;	
			enable_irq(AR7240_GPIO_IRQn(BUTTON_GPIO));
		}
		
	}
	if(buttons_dev.button_short_time==1)
	{
		buttons_dev.press_cnt = 1; /* 按键计数加1    */
		buttons_dev.ev_press = 1; 
		buttons_dev.button_short_time = 0;
		sec =0;	
		printk("buttons_dev.press_cnt:%d\n",buttons_dev.press_cnt);
		//wake_up_interruptible(&(buttons_dev.buttons_waitqueue));   /* 唤醒休眠的进程 */				
	}
}

//中断响应函数,到这里表明已经发生了中断,进入了中断处理函数。
static irqreturn_t ar7242buttons_interrupt(int irq, void *dev_id)
{
	disable_irq(AR7240_GPIO_IRQn(BUTTON_GPIO));		//此处关闭中断,之后,系统不会再次响应中断。
	OS_SET_TIMER(&os_timer_t, 0);	
//	printk("您请求按键中断一次,系统为您做出按键响应,请等待.................\n");
	return IRQ_HANDLED;
}


static int ar7242Button_open(struct inode *inode, struct file *file)
{
	int ret =0;
	if (MINOR(inode->i_rdev) != BUTTON_MINOR) 
	{
		return -ENODEV;
	}

	if (buttons_dev.ar7242_ButtonOpened) 
	{
		return -EBUSY;
	}

	buttons_dev.ar7242_ButtonOpened = 1;
	ret = request_irq(AR7240_GPIO_IRQn(BUTTON_GPIO), ar7242buttons_interrupt, 0,"ar7242 button", NULL);
	if (ret != 0) 
	{
		printk("request_irq for button  (error %d)\n", ret);
	}	
	return ret;
}

static int ar7242Button_close(struct inode *inode, struct file *file)
{
	if (MINOR(inode->i_rdev) != BUTTON_MINOR) 
	{
		return -ENODEV;
	}
	buttons_dev.ar7242_ButtonOpened = 0;
	free_irq(ATH_GPIO_IRQn(BUTTON_GPIO), NULL);
	return 0;
}

static ssize_t ar7242Button_read(struct file *file, char *buff, size_t count, loff_t * ppos)
{
	unsigned long err;
	int retval = 0;
	
	/* 如果ev_press等于0,休眠 *///应用程序需要读取按键的状态,故将这句屏蔽,不然应用程序会阻塞到这里。
	//即读不到数据的时候直接返回(-EFAULT)
//	wait_event_interruptible(buttons_dev.buttons_waitqueue, (buttons_dev.ev_press != 0));

	/* 执行到这里时,ev_press等于1,将它清0 */
	buttons_dev.ev_press = 0;

	/* 将按键状态复制给用户,并清0 */
	err = copy_to_user(buff,(const void *)&buttons_dev.press_cnt,sizeof(buttons_dev.press_cnt));
  if ((err < 0)) 
	{
		//printk("\ncopy_to_user error ret: %d\n",err);	

		retval = -EFAULT;
	}else
	{
		retval = sizeof(buttons_dev.press_cnt);
	}
	
  // buttons_dev.press_cnt =0;
   //当应用程序读到按键的次数>1后,表示按键已经按下。可以根据此值,做出下一步的判断。
	//printk("\nretval:%d  buttons_dev.press_cnt: %d: buttons_dev.ev_press:%d\n",retval,buttons_dev.press_cnt ,buttons_dev.ev_press);	
	
	return retval;
}

static ssize_t ar7242Button_write(struct file *file, const char *buf, size_t count, loff_t * ppos)
{
	unsigned  int cnt =0;
	int ret ;
	int __user *ptr = (int __user *)buf;
	ret = get_user(cnt, ptr);
	if(ret)
	{
		ret= -EFAULT;
	}
	else
	{
		ret = count;
		
	}
	buttons_dev.press_cnt = 0;
	//printk("\rret: %d  buttons_dev.press_cnt: %d: cnt:%d  count;%d\n",ret,buttons_dev.press_cnt ,cnt,count);	
	
	return ret;
}

static int ar7242Button_ioctl(struct inode *inode, struct file *file, unsigned int cmd,unsigned long arg)
{
	int ret = 0;
	return ret;
}

static struct file_operations ar7242Button_fops = {
	read:	    ar7242Button_read,
	write:		ar7242Button_write,
	ioctl:		ar7242Button_ioctl,
	open:		ar7242Button_open,
	release:	ar7242Button_close
};

static struct miscdevice athfr_miscdev = {
	BUTTON_MINOR,
	"ar7242_button",
	&ar7242Button_fops
};


static int __init ar7242_button_init(void)
{

	int ret;
	
	ret = misc_register(&athfr_miscdev);
	if (ret < 0) 
    {
		printk("*** insmod ar7242_button.ko failed %d *** \n", ret);
		return -1;
	}
	OS_INIT_TIMER(NULL, &os_timer_t, wps_led_blink, &os_timer_t);
	init_waitqueue_head(&(buttons_dev.buttons_waitqueue));
	
	ar7240_gpio_config_input(BUTTON_GPIO);
	ar7240_gpio_config_int(BUTTON_GPIO, INT_TYPE_LEVEL,INT_POL_ACTIVE_LOW);
    
	printk("*** insmod ar7242_button.ko success *** \n");
	return ret;
	
}

		
static void __exit ar7242_buttons_exit(void)
{       

	  misc_deregister(&athfr_miscdev);
	// ath_gpio_intr_shutdown(ATH_GPIO_IRQn(BUTTON_GPIO));
	  printk("%s (%s) line: %d\n", __FILE__, __func__,__LINE__);			
}		
module_init(ar7242_button_init);
module_exit(ar7242_buttons_exit);
MODULE_AUTHOR("cctv from test");                // 驱动程序的作者
MODULE_DESCRIPTION("button driver for Atheros 7242  platform");         // 一些描述信息
MODULE_LICENSE("GPL");                              // 遵循的协议




  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
Linux 定时器 timer_list 是一个内核数据结构,用于管理内核中的定时器。它是一个双向链表,每个节点表示一个定时器timer_list 的定义位于 `<linux/timer.h>` 头文件中。 每个 timer_list 节点的定义如下: ```c struct timer_list { struct list_head entry; // 定时器节点的链表指针 unsigned long expires; // 定时器的到期时间 void (*function)(unsigned long); // 定时器回调函数 unsigned long data; // 传递给回调函数的参数 struct tvec_base *base; // 定时器所属的时间轮 int slack; // 定时器的松弛时间 }; ``` 其中,`entry` 是一个 `list_head` 结构,用于将节点连接到定时器链表中。`expires` 表示定时器的到期时间,以 jiffies 单位表示。`function` 是定时器的回调函数,在定时器到期时被调用。`data` 是传递给回调函数的参数。`base` 表示定时器所属的时间轮,`slack` 是定时器的松弛时间,用于处理定时器的精度。 在使用 timer_list 时,可以使用以下函数进行初始化和操作: - `timer_setup(struct timer_list *timer, void (*function)(unsigned long), unsigned int flags)`:初始化一个定时器,并指定回调函数和标志。 - `init_timer(struct timer_list *timer)`:初始化一个定时器。 - `add_timer(struct timer_list *timer)`:将定时器添加到定时器链表中。 - `del_timer(struct timer_list *timer)`:从定时器链表中删除定时器。 - `mod_timer(struct timer_list *timer, unsigned long expires)`:修改定时器的到期时间。 这些函数可以通过 `<linux/timer.h>` 头文件中的宏来调用。通过操作 timer_list,可以实现在 Linux 内核中的定时器功能。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

家有工程师

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

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

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

打赏作者

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

抵扣说明:

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

余额充值