AT指令框架之自定义

简言:

  • 使用NB或BLE或者AT指令交互的模组时,需要一个方便灵活的AT指令框架

  • 便于快速的实现功能,后期维护扩展功能

  • 思路

  • 使用状态机原理和二维数组存储指令数据

  • 按照指令集顺序执行命令

  • 分享的友情链接
    https://blog.csdn.net/weixin_42192405/article/details/115765164

(另有一种AT Command 指令处理框架,本文框架与AT Command各有优劣)
AT Command使用教程全网有很多了,后续会更新AT command示例。

代码示例:

  • AT状态机处理
#include "app_iot_at.h"

extern rt_mailbox_t iot_mb_at;
extern rt_mailbox_t iot_mb_ctl;
extern rt_uint8_t iot_read_buff[IOT_READ_BUFF_SIZE];
extern rt_uint8_t iot_read_len;
extern rt_uint8_t iot_send_buff[IOT_SEND_BUFF_SIZE];
extern iot_device_message_t  i_dev_mess;

extern rt_timer_t iot_timer_2;
//临时测试数据
//const uint8_t temp_70[] = {"AT+MCWSEND=41,00FF2401010023435965676000121C20010B00FF0000000000C800640000330100025EBAED06F1C857\r\n"};

/*
 * 清单号
 */
rt_uint8_t i_list_num =0;
/*
 * 重试
 */
rt_uint8_t i_list_retry =0;
/*
 * 清单选择
 */
iot_cmd_list_t *i_list_select = NULL;

/*
 * 当前变量
 */
iot_dev_state_t i_cur = {0,0,0,0,0,0};

/*
 * 特定清单
 */
/*
 * 连接中国电信初始化
 */
iot_cmd_list_t  iot_list_poweron_lwm2m[]=
{I_AT, I_AT, I_QSCLK, I_CGMI, I_CGSN, I_ATE, I_QBAND, I_CGPADDR, I_NNMI, I_NCFG, I_CSCON1, I_CSCON, I_CEREG, I_NCDPOPEN, I_CSQ, I_NMSTATUS,  I_FINISH};
/*
 *上传数据
 */
iot_cmd_list_t iot_list_updata_lwm2m[]=
{ I_CSQ, I_NMGS, I_FINISH};

/*
 * 信号强度
 */
iot_cmd_list_t iot_list_csq[]=
{ I_CSQ, I_CCLK, I_FINISH};

/*
 * 连接客户后台初始化
 */
iot_cmd_list_t  iot_list_poweron_tcpip[]=
{I_AT, I_QSCLK, I_QICLOSE,  I_CGMI, I_CGSN, I_ATE, I_QBAND, I_CSCON1, I_CSCON, I_CEREG, I_CGPADDR,
       I_CSQ, I_CCLK, I_QENG, I_FINISH};
/*
 * 上传数据
 */
iot_cmd_list_t iot_list_updata_tcpip[]=
{ I_QIOPEN, I_QICFG, I_QISEND, I_FINISH};

/*
 * 列表
 */
iot_dev_state_t iot_cmds[] =
{
	//指令=指令+应答+等待时间+重试次数+处理
	{I_AT_NULL, "NULL", "NULL", 0, 0, NULL},
	{I_AT, "AT\r\n", "OK", 1000, 3, i_default_deal},
	{I_QRST, "AT+QRST=1\r\n", "+IP", 15000, 3, i_default_deal},
	{I_QSCLK, "AT+QSCLK=0\r\n", "OK", 1000, 3, i_default_deal},
	{I_CGMI, "AT+CGMI\r\n", "OK", 1000, 3, i_default_deal},
	{I_CGSN, "AT+CGSN=1\r\n", "OK", 1000, 3, i_default_deal},
	{I_ATE, "ATE0\r\n", "OK", 1000, 3, i_default_deal},
	{I_QBAND, "AT+QBAND=0\r\n", "OK", 1000, 3, i_default_deal},
	{I_CGPADDR, "AT+CGPADDR?\r\n", "OK", 1000, 3, i_default_deal},
	{I_NNMI, "AT+NNMI=1\r\n", "OK", 1000, 3, i_default_deal},
	{I_NCFG, "AT+NCFG=0,300\r\n", "OK", 1000, 3, i_default_deal},
	{I_CSCON1, "AT+CSCON=1\r\n", "OK", 1000, 3, i_default_deal},
	{I_CSCON, "AT+CSCON?\r\n", "OK", 1000, 3, i_default_deal},
	{I_CEREG, "AT+CEREG?\r\n", "OK", 1000, 3, i_default_deal},
	{I_NCDPOPEN, "AT+NCDPOPEN=\"221.229.214.202\",5683\r\n", "+QLWEVTIND: 3", 5000, 3, i_default_deal},
	{I_CSQ, "AT+CSQ\r\n", "OK", 1000, 3, i_csq_deal},
	{I_NMSTATUS, "AT+NMSTATUS?\r\n", "REGISTERED_AND_OBSERVED", 1000, 3, i_default_deal},
	{I_NMGS, iot_send_buff, "OK", 5000, 3, i_default_deal},

	{I_QIOPEN, "AT+QIOPEN=0,0,\"TCP\",\"47.94.169.135\",7191\r\n", "+QIOPEN: 0,0", 5000, 3, i_default_deal},
	//{I_QIOPEN, "AT+QIOPEN=0,0,\"TCP\",\"36.137.226.30\",43588\r\n", "+QIOPEN: 0,0", 5000, 3, i_default_deal},
	{I_QICFG, "AT+QICFG=\"dataformat\",1,1\r\n", "OK", 5000, 3, i_default_deal},
	{I_QISEND, iot_send_buff, "OK", 5000, 3, i_default_deal},
	{I_QICLOSE, "AT+QICLOSE=0\r\n", "CLOSE OK", 30000, 3, i_default_deal},
	{I_CCLK, "AT+CCLK?\r\n", "+CCLK", 3000, 3, i_rtc_deal},
	{I_QENG, "AT+QENG=0\r\n", "OK", 3000, 3, i_qeng_deal},
};


/*
 * 重置
 */
void i_cur_reset(void)
{
	i_cur.i_cur_cmd = I_AT_NULL;
	i_cur.i_send = NULL;
	i_cur.i_ack = NULL;
	i_cur.i_wait_time = 0;
	i_cur.i_try_cnt = 0;
	i_cur.i_recv_deal = NULL;

	i_list_num =0;
	i_list_retry =0;
	i_list_select = NULL;
}
/*
 * 默认处理
 */
uint8_t i_default_deal(char* data, uint8_t len)
{
	return 1;
}
/*
 * 信号强度
 */
uint8_t i_csq_deal(char* data, uint8_t len)
{
    uint8_t *data_p = data;
    uint8_t csq_value = 0;
    if(strstr(data_p, "+CSQ") != NULL)
    {
        LOG_OUT("%c\r\n", *(data_p+8));
        LOG_OUT("%c\r\n", *(data_p+9));

        csq_value = (*(data_p+8) - '0')*10 + (*(data_p+9) - '0');
        LOG_OUT("csq_value =%d\r\n", csq_value);


        i_dev_mess.csq = csq_value;
    }

    return 1;
}
/*
 * RTC处理
 */
uint8_t i_rtc_deal(char* data, uint8_t len)
{
    uint8_t *data_p = data;
    uint8_t temp =0;
    uint8_t year =0;
    uint8_t Month =0;
    uint8_t Date =0;

    uint8_t Hours =0;
    uint8_t Minutes =0;
    uint8_t Seconds =0;

    uint32_t timestamp = 0;

    if(strstr(data_p, "+CCLK") != NULL)
    {
        year = *(data_p+12) - '0';
        year = year*10 + (*(data_p+13) - '0');
        LOG_OUT("year=%d\r\n", year);
        year = dec_transition_hex_show(year);

        Month = *(data_p+15) - '0';
        Month = Month*10 + (*(data_p+16) - '0');
        LOG_OUT("Month=%d\r\n", Month);
        Month = dec_transition_hex_show(Month);

        Date = *(data_p+18) - '0';
        Date = Date*10 + (*(data_p+19) - '0');
        LOG_OUT("Date=%d\r\n", Date);
        Date = dec_transition_hex_show(Date);

        Hours = *(data_p+21) - '0';
        Hours = Hours*10 + (*(data_p+22) - '0');
        LOG_OUT("Hours=%d\r\n", Hours);
        Hours = dec_transition_hex_show(Hours);

        Minutes = *(data_p+24) - '0';
        Minutes = Minutes*10 + (*(data_p+25) - '0');
        LOG_OUT("Minutes=%d\r\n", Minutes);
        Minutes = dec_transition_hex_show(Minutes);


        Seconds = *(data_p+27) - '0';
        Seconds = Seconds*10 + (*(data_p+28) - '0');
        LOG_OUT("Seconds=%d\r\n", Seconds);
        Seconds = dec_transition_hex_show(Seconds);

        rtc_receive_calibration(Hours, Minutes, Seconds, year, Month, Date);
        app_rtc_data_read(&timestamp);

        i_dev_mess.collect_time[0] = (timestamp >>24) & 0xFF;
        i_dev_mess.collect_time[1] = (timestamp >>16) & 0xFF;
        i_dev_mess.collect_time[2] = (timestamp >>8) & 0xFF;
        i_dev_mess.collect_time[3] = (timestamp ) & 0xFF;
    }
    return 1;
}

//+QENG: 0,3688,11,264,"09018613",-84,-8,-77,6,8,"4B03",0,21,3
//-6, -66,
//6, 66,
/*
 * 参考信号强度等
 */
uint8_t i_qeng_deal(char* data, uint8_t len)
{
    uint8_t *data_p = data;
    uint8_t cnt =0;
    uint16_t pci = 0;
    uint16_t rsrp = 0;
    uint16_t snr = 0;

    for(uint8_t t=0; t<60; t++)
    {
        if( *(data_p+t) == ',')
        {
            cnt++;

            if(cnt == 3)
            {
                if(*(data_p+t+4) == ',')
                {
                    pci = (*(data_p+t+1)-'0')*100 + (*(data_p+t+2)-'0')*10 + (*(data_p+t+3)-'0');
                }
                else if(*(data_p+t+3) == ',')
                {
                    pci = (*(data_p+t+1)-'0')*10 + (*(data_p+t+2)-'0');
                }
                else if(*(data_p+t+2) == ',')
                {
                    pci = (*(data_p+t+1)-'0');
                }
                LOG_OUT("pci=%d\r\n", pci);

                i_dev_mess.pci[0] = pci >>8;
                i_dev_mess.pci[1] = pci &0xFF;
            }
            else if(cnt == 5)
            {
                if(*(data_p+t+1) == '-')
                {

                    if(*(data_p+t+5) == ',')
                    {
                        rsrp = (*(data_p+t+2)-'0')*100 + (*(data_p+t+3)-'0')*10 + (*(data_p+t+4)-'0');
                    }
                    else if(*(data_p+t+4) == ',')
                    {
                        rsrp = (*(data_p+t+2)-'0')*10 + (*(data_p+t+3)-'0');
                    }
                    else if(*(data_p+t+3) == ',')
                    {
                        rsrp = (*(data_p+t+2)-'0');
                    }

                    LOG_OUT("rsrp=%d\r\n", rsrp);
                    if(rsrp >= 141)
                    {
                        rsrp = 0;
                    }
                    else
                    {
                        rsrp = 141 - rsrp;
                    }

                    LOG_OUT("rsrp=%d\r\n", rsrp);

                    i_dev_mess.rsrp[0] = rsrp >>8;
                    i_dev_mess.rsrp[1] = rsrp &0XFF;
                }

            }
            else if(cnt == 8)
            {
                if(*(data_p+t+1) == '-')
                {
                    if(*(data_p+t+3) == ',')
                    {
                        snr = (*(data_p+t+2)-'0');
                    }
                    else if(*(data_p+t+4) == ',')
                    {
                        snr = (*(data_p+t+2)-'0')*10 + (*(data_p+t+3)-'0');
                    }
                    LOG_OUT("snr=%d\r\n", snr);
                    snr |= 0x8000;
                    LOG_OUT("snr=%x\r\n", snr);
                }
                else
                {
                    if(*(data_p+t+2) == ',')
                    {
                        snr =(*(data_p+t+1)-'0');
                    }
                    else if(*(data_p+t+3) == ',')
                    {
                        snr = (*(data_p+t+1)-'0')*10 + (*(data_p+t+2)-'0');
                    }
                    LOG_OUT("snr=%d\r\n", snr);
                }

                i_dev_mess.snr[0] = snr >>8;
                i_dev_mess.snr[1] = snr &0XFF;
            }
        }
    }


    return 1;
}


/*
 * 超时重发
 */
void iot_timeout_task_2(void *parameter)
{
	LOG_OUT("iot_timeout_task_2\r\n");
	rt_mb_send_wait(iot_mb_at, I_AT_TIMEOUT, 1000);
}
/*
 * AT线程任务
 * 操作系统使用线程邮箱等待事件触发
 * 裸机使用状态标志位等待事件触发
 * 避免延时,加快速度
 */
void iot_at_thread_task(void *parameter)
{
    rt_thread_mdelay(100);
	LOG_OUT("iot_at_thread_task OK\r\n");

	rt_uint32_t at_cmd = 0;
	rt_uint32_t at_ret = 0;
    while (1)
    {
    	at_ret = rt_mb_recv(iot_mb_at, &at_cmd, RT_WAITING_FOREVER);
    	if(at_ret == RT_EOK)
    	{
    		LOG_OUT("at_cmd=%d\r\n", at_cmd);

    		switch(at_cmd)
    		{
    		    //空闲
    			case I_AT_IDLE:
    			{
    				LOG_OUT("I_AT_IDLE\r\n");
    			}
    			break;
    			//发送
    			case I_AT_SEND:
    			{
    				LOG_OUT("I_AT_SEND=");
    				i_cur = iot_cmds[i_list_select[i_list_num]];
    				i_list_retry = 0;

    				if( i_list_select[i_list_num] != I_FINISH)
    				{
    					rt_device_write(app_iot_dev, RT_NULL, i_cur.i_send, strlen(i_cur.i_send));
    					rt_timer_control(iot_timer_2, RT_TIMER_CTRL_SET_TIME, &i_cur.i_wait_time);
    					rt_timer_start(iot_timer_2);
    					LOG_OUT("%s\r\n", i_cur.i_send);
    				}
    				else
    				{
    					LOG_OUT("I_FINISH\r\n");
    					rt_mb_send_wait(iot_mb_at, I_AT_FINISH, 1000);
    				}
    			}
    			break;
    			//等待
    			case I_AT_WAIT:
    			{
    				LOG_OUT("I_AT_WAIT:");
    				memset(iot_read_buff, 0x00, sizeof(iot_read_buff));
    				while( (iot_read_len = rt_device_read(app_iot_dev, RT_NULL, iot_read_buff, RT_NULL)) > 0)
    				{
    					LOG_OUT("iot_read_len=%d\r\n", iot_read_len);
    					LOG_OUT("I_AT_REC=");
    					for(uint8_t t=0; t<iot_read_len; t++)
    					{
    						LOG_OUT("%c",iot_read_buff[t]);
    					}
    					LOG_OUT("\r\n");
    					if(i_cur.i_send != NULL)
						{
							if(strstr(iot_read_buff, i_cur.i_ack) != NULL)
							{
								if(i_cur.i_recv_deal(iot_read_buff, iot_read_len) == 1)
								{
									LOG_OUT("iot ack ok\r\n");
									rt_timer_stop(iot_timer_2);
									i_list_num++;
									rt_mb_send_wait(iot_mb_at, I_AT_SEND, 1000);
								}
							}
		                    else if(i_cur.i_ack == NULL)
                            {
                                LOG_OUT("null ack ok\r\n");
                                rt_timer_stop(iot_timer_2);
                                i_list_num++;
                                rt_mb_send_wait(iot_mb_at, I_AT_SEND, 1000);
                            }
						}
    					else
    					{

    						LOG_OUT("URC:\r\n");

    						if(iot_ctl.i_module == I_LWM2M)
    						{
                                if(strstr(iot_read_buff, "+NNMI" ) != NULL)
                                {
                                    i_urc_deal(iot_read_buff, iot_read_len);
                                }
    						}
    						else
    						{
                                if(strstr(iot_read_buff, "+QIURC: \"recv\"" ) != NULL)
                                {
                                    i_urc_deal(iot_read_buff, iot_read_len);
                                }
                                else if( strstr(iot_read_buff, "+QIURC:\"closed\"" ) != NULL )
                                {
                                    LOG_OUT("+QIURC: closed deal\r\n");

                                    iot_ctl.i_send_cmd = I_CMD_UPDATA;
                                    rt_mb_send_wait(iot_mb_at, I_AT_SEND, 1000);
                                }
                            }
    					}
    				}
    			}
    			break;
    			//超时
    			case I_AT_TIMEOUT:
    			{
    				LOG_OUT("I_AT_TIMEOUT:");
    				LOG_OUT("retry:%d < %d\r\n", i_list_retry, i_cur.i_try_cnt);
    				if(i_list_retry++ < i_cur.i_try_cnt)
    				{
    					rt_device_write(app_iot_dev, RT_NULL, i_cur.i_send, strlen(i_cur.i_send));
    					rt_timer_control(iot_timer_2, RT_TIMER_CTRL_SET_TIME, &i_cur.i_wait_time);
    					rt_timer_start(iot_timer_2);
    					LOG_OUT("%s\r\n", i_cur.i_send);
    				}
    				else
    				{
    					rt_mb_send_wait(iot_mb_at, I_AT_FAIL, 1000);
    				}
    			}
    			break;
    			//完成
    			case I_AT_FINISH:
    			{
    				LOG_OUT("I_AT_FINISH\r\n");
    				i_cur_reset();
    				rt_mb_send_wait(iot_mb_ctl, I_EVT_RET_SUCC, 1000);
    			}
    			break;
    			//失败
    			case I_AT_FAIL:
    			{
    				LOG_OUT("I_AT_FAIL\r\n");
    				i_cur_reset();
    				rt_mb_send_wait(iot_mb_ctl, I_EVT_RET_FAIL, 1000);
    			}
    			break;
    		}
    	}
    }
}





  • 定义的状态变量
#ifndef __APP_IOT_AT_H
#define __APP_IOT_AT_H

#include "app_user.h"

//AT任务事件
typedef enum
{
	I_AT_IDLE 	 = 1 << 0,
	I_AT_SEND	 = 1 << 1,
	I_AT_WAIT	 = 1 << 2,
	I_AT_TIMEOUT = 1 << 3,
	I_AT_FINISH  = 1 << 4,
	I_AT_FAIL	 = 1 << 5,
}IOT_AT_EVENT_T;


/*
 * 针对某NB模组的AT指令
 */
typedef enum
{
	I_AT_NULL = 0,
	I_AT,
	I_QRST,
	I_QSCLK,
	I_CGMI,
	I_CGSN,
	I_ATE,
	I_QBAND,
	I_CGPADDR,
	I_NNMI,
	I_NCFG,
	I_CSCON1,
	I_CSCON,
	I_CEREG,
	I_NCDPOPEN,
	I_CSQ,
	I_NMSTATUS,
	I_NMGS,
	I_QIOPEN,
	I_QICFG,
	I_QISEND,
	I_QICLOSE,
	I_CCLK,
	I_QENG,

	I_FINISH

}iot_cmd_list_t;

/*
 * 控制
 */
typedef struct
{
	iot_cmd_list_t i_cur_cmd;//当前指令
	uint8_t*	i_send;//发送指令
	uint8_t*	i_ack;//应答数据
	int 		i_wait_time;//等待时间
	int			i_try_cnt;//重试次数
	uint8_t		(*i_recv_deal)(uint8_t* data, uint8_t len);//处理数据
}iot_dev_state_t;


uint8_t i_default_deal(char* data, uint8_t len);
uint8_t i_csq_deal(char* data, uint8_t len);
uint8_t i_rtc_deal(char* data, uint8_t len);
uint8_t i_qeng_deal(char* data, uint8_t len);


#endif

  • 控制AT执行流程的事件
    		    //空闲
    			case I_EVT_IDLE:
    			{
    				LOG_OUT("I_EVT_IDLE\r\n");
    			}
    			break;
    			//上电
    			case I_EVT_POWERON:
    			{
    				LOG_OUT("I_EVT_POWERON\r\n");
    				memset(iot_read_buff, 0x00, sizeof(iot_read_buff));
    				while( (iot_read_len = rt_device_read(app_iot_dev, RT_NULL, iot_read_buff, RT_NULL)) > 0)
    				{
    					LOG_OUT("iot_read_len=%d\r\n", iot_read_len);
    					for(uint8_t t=0; t<iot_read_len; t++)
    					{
    						LOG_OUT("%c",iot_read_buff[t]);
    					}
    					LOG_OUT("\r\n");
    					//IP入网
    					if((strstr(iot_read_buff, "+IP" ) != NULL))
    					{
    						LOG_OUT("readly iot_list_poweron\r\n");
    						iot_ctl.i_run_ste = I_WORK;
    						i_cur_reset();

    						if(iot_ctl.i_module == I_LWM2M)
    						{
    						 i_list_select = iot_list_poweron_lwm2m;
    						}
    						else
    						{
    						    i_list_select = iot_list_poweron_tcpip;
                            }
    						iot_ctl.i_send_cmd = I_CMD_POWER;
    						rt_mb_send_wait(iot_mb_at, I_AT_SEND, 1000);
    					}
    				}
    			}
    			break;
    			//返回成功
    			case I_EVT_RET_SUCC:
    			{
    				LOG_OUT("I_EVT_RET_SUCC\r\n");
    				LOG_OUT("send_cmd=%d\r\n", iot_ctl.i_send_cmd);
    				switch(iot_ctl.i_send_cmd)
    				{
    				    //上电事件
    					case I_CMD_POWER:
    					{

    					    LOG_OUT("I_CMD_POWER\r\n");
                            //上报标志
                            if(iot_ctl.i_up_flag == true)
                            {
                                iot_ctl.i_up_flag = false;

                                //上传数据
                                app_iot_up_data_update();

                                LOG_OUT("reday updata\r\n");
                                i_cur_reset();
                                if(iot_ctl.i_module == I_LWM2M)
                                {
                                    i_list_select = iot_list_updata_lwm2m;
                                }
                                else
                                {
                                    i_list_select = iot_list_updata_tcpip;
                                }

                                iot_ctl.i_send_cmd = I_CMD_UPDATA;
                                rt_mb_send_wait(iot_mb_at, I_AT_SEND, 1000);
                            }
                            else
                            {
                                LOG_OUT("bsp_iot_close\r\n");
                                bsp_iot_close();

                            }
    					}
    					break;
    					//结束事件
    					case I_CMD_UPDATA:
    					{
    						LOG_OUT("send data success\r\n");

                            LOG_OUT("bsp_iot_close\r\n");
                            bsp_iot_close();

    					}
    					break;
    				}
    			}
    			break;
    			//失败关闭
    			case I_EVT_RET_FAIL:
    			{
    				LOG_OUT("I_EVT_RET_FAIL\r\n");
    				LOG_OUT("send_cmd=%d\r\n", iot_ctl.i_send_cmd);

                    LOG_OUT("bsp_iot_close\r\n");
                    bsp_iot_close();
    			}
    			break;
  • 控制流程变量
#ifndef __APP_IOT_CTL_H
#define __APP_IOT_CTL_H

#include "app_user.h"



typedef enum
{
	I_POWERON = 0,
	I_WORK,
	I_SLEEP,
	I_ALARM
}IOT_RUN_STATE_T;

typedef enum
{
	I_CMD_POWER = 0,
	I_CMD_UPDATA ,

}IOT_SEND_CMD_T;

typedef enum
{
    I_LWM2M = 0,
    I_TCP_IP,
}I_MODULE_TYPE_T;


typedef struct
{
	IOT_RUN_STATE_T i_run_ste;
	IOT_SEND_CMD_T i_send_cmd;
	uint8_t    i_up_flag;
	uint8_t    i_module;


}iot_control_t;



//EVT任务事件
typedef enum
{
	I_EVT_IDLE       		= 1 << 0,//空闲
	I_EVT_POWERON 	 		= 1 << 1,//电源上电事件

	I_EVT_RET_SUCC   		= 1 << 2,//返回成功事件
	I_EVT_RET_FAIL   		= 1 << 3,//返回失败事件

	I_EVT_UPDATA_SUCC       = 1 << 4,//上传成功事件
	I_EVT_UPDATA_FAIL       = 1 << 5,//上传失败事件

	I_EVT_RTC_UPDATA        = 1 << 6,//RTC触发事件

	I_EVT_SENSOR_REC        = 1 << 7,//传感器反馈结果事件
	I_EVT_SENSOR_SEND       = 1 << 8,//传感器控制参数事件

}IOT_EVT_EVENT_T;



extern iot_control_t iot_ctl;
void iot_parameter_init(void);


#endif

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值