nrf24l01linux下的驱动程序

本文档提供了Linux环境下针对nRF24L01无线通信模块的设备驱动程序代码,包括初始化、读写操作、中断处理等关键函数。代码适用于2.6.24版本的Linux内核,ARM平台为博创“三剑客”up—Star2410。驱动程序涉及GPIO配置、SPI通信、中断处理等功能,实现了数据的发送和接收。

最近太多的网友发邮件给我要nrf24l01的驱动程序 现在贴出来供需要的人参考

/*******************************************************************************
* 此驱动程序是linux下nrf24l01的设备驱动程序,驱动程序由两部分组成:
* 1、nrf24l01.c
* 2、nrf24l01.h ,linux版本是2.6.24,ARM平台为博创“三剑客”up—Star2410
* Author:dengtao_deng
* Email:linuxembe@126.com
* QQ:990027753 Embed
* nrf24l01.c
*******************************************************************************/
#include <linux/irq.h>
#include <linux/miscdevice.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/major.h>
#include <asm/irq.h>
#include <linux/interrupt.h>
#include <linux/kernel.h>//printk()
#include <linux/module.h>
#include <linux/init.h>
#include <linux/mm.h>
#include <linux/fs.h>
#include <linux/types.h>//u8,u16,u3……
#include <linux/delay.h>
#include <linux/moduleparam.h>
#include <linux/slab.h>
#include <linux/sched.h>//和任务相关
#include <linux/errno.h>
#include <linux/ioctl.h>
#include <linux/cdev.h>
#include <linux/string.h>
#include <linux/list.h>
#include <linux/pci.h>
#include <asm/uaccess.h>//copy_to_user(),copy_from_user()
#include <asm/atomic.h>
#include <asm/unistd.h>
#include <linux/spinlock.h>
#include <asm/system.h>
#include <asm/io.h> //readx(), writex()
#include <linux/ioport.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/miscdevice.h>
#include <linux/delay.h>
#include <asm/irq.h>
#include <asm/arch/regs-gpio.h>
#include <asm/hardware.h>
#include "nrf24l01.h"

#define RX_DR SPI_Read(STATUS)&1

uchar TX_ADDRESS[]={0x34,0x43,0x10,0x10,0x01};//这个应该是要发送到的地址,不确定
uchar RX_ADDRESS[]={0x34,0x43,0x10,0x10,0x01};//接收地址

/********************************************************************************************************/
#define NRF_NAME "S3C2440_NRF"  //设备驱动名称
static int NRF_MAJOR = 258;     //主设备号
struct nrf_dev
{
 struct cdev cdev;
 char dataTx[TX_PLOAD_WIDTH];
 char dataRx[RX_PLOAD_WIDTH];
};
struct nrf_dev *nrf_devp;
/********************************************************************************************************/

//底层驱动函数
ulong status_32; //暂存状态32位

//初始化各种IO口的输入、输出设置
void GPIO_CONFIG_PINS(void){
 s3c2410_gpio_cfgpin(S3C2410_GPB0,S3C2410_GPIO_OUTPUT); //CSN --TOUT0
 s3c2410_gpio_cfgpin(S3C2410_GPB1,S3C2410_GPIO_OUTPUT); //MOSI--TOUT1
 s3c2410_gpio_cfgpin(S3C2410_GPB4,S3C2410_GPIO_OUTPUT); //SCK---TCLK0
 s3c2410_gpio_cfgpin(S3C2410_GPG11,S3C2410_GPIO_OUTPUT);//CE----TCLK1
 s3c2410_gpio_cfgpin(S3C2410_GPH9,S3C2410_GPIO_INPUT);  //IRQ---CLKOUT0
 s3c2410_gpio_cfgpin(S3C2410_GPE7,S3C2410_GPIO_INPUT);  //MISO--SDDATA0
}
//延时函数
void delay(int n)
{
 int m,k;
 for(m=0;m<10;m++)
  for(k=0;k<n;k++){}
}
// 写一个字节到24L01,同时读出一个字节
uchar SPI_RW(uchar byte)
{
 uchar bit_ctr;
 for(bit_ctr=0;bit_ctr<8;bit_ctr++) // output 8-bit
 {
  if(byte & 0x80)
   s3c2410_gpio_setpin(S3C2410_GPB1,1); //MOSI
  else
   s3c2410_gpio_setpin(S3C2410_GPB1,0); //MOSI
  byte = (byte << 1); // shift next bit into MSB..
  s3c2410_gpio_setpin(S3C2410_GPB4,1);//SCK
  if(s3c2410_gpio_getpin(S3C2410_GPE7))
  {  //MISO
   byte |= 1<<0;
  }else
  {
   byte |= 0<<0;
  }
  s3c2410_gpio_setpin(S3C2410_GPB4,0); //SCK
 }
 return(byte); // return read byte
}

/******************************************************************************
* 读取寄存器值的函数:基本思路就是通过READ_REG 命令(也就是0x00+寄存器地址),把
* 寄存器中的值读出来。对于函数来说也就是把reg 寄存器的值读到reg_val 中去。
******************************************************************************/
uchar SPI_Read(uchar reg)
{
 uchar reg_val;
 s3c2410_gpio_setpin(S3C2410_GPB0,0); //CSN
 SPI_RW(reg); // Select register to read from..
 reg_val = SPI_RW(0xff); // ..then read registervalue
 s3c2410_gpio_setpin(S3C2410_GPB0,1); //CSN
 return(reg_val); // return register value
}

/****************************************
* 向寄存器reg写一个字节,同时返回状态字节
*****************************************/
uchar SPI_RW_Reg(uchar reg, uchar value)
{
 uchar status;
 s3c2410_gpio_setpin(S3C2410_GPB0,0); //CSN
 status = SPI_RW(reg); // select register
 SPI_RW(value); // ..and write value to it..
 s3c2410_gpio_setpin(S3C2410_GPB0,1); //CSN
 return(status); // return nRF24L01 status byte
}

/***************************************
*读出bytes字节的数据
****************************************/
uchar SPI_Read_Buf(uchar reg, uchar *pBuf, uchar bytes)
{
 uchar status,byte_ctr;
 s3c2410_gpio_setpin(S3C2410_GPB0,0); //CSN
 status = SPI_RW(reg); // Select register to write to and read status byte
 for(byte_ctr=0;byte_ctr<bytes;byte_ctr++)
 pBuf[byte_ctr] = SPI_RW(0);
 s3c2410_gpio_setpin(S3C2410_GPB0,1); //CSN
 return(status); // return nRF24L01 status byte
}

/*************************************
*写入bytes字节的数据
**************************************/
uchar SPI_Write_Buf(uchar reg, uchar *pBuf, uchar bytes)
{
 uchar status,byte_ctr;
 s3c2410_gpio_setpin(S3C2410_GPB0,0); //CSN
 status = SPI_RW(reg);
 for(byte_ctr=0; byte_ctr<bytes; byte_ctr++)
 SPI_RW(*pBuf++);
 s3c2410_gpio_setpin(S3C2410_GPB0,1); //CSN
 return(status);
}
/*************************************
* 启动NRF24L01发送一次数据
* txbuf:待发送数据首地址
* 返回值:发送完成状况
**************************************/
uchar TxPacket(uchar *txbuf)
{
 uchar sta;
 int i=0;
 printk("TxPacket\n");
 for(i=0;i<10;i++)
  s3c2410_gpio_setpin(S3C2410_GPG11,0); //CE
  SPI_Write_Buf(WR_TX_PLOAD,txbuf,TX_PLOAD_WIDTH);//写数据到TX BUF  32个字节
  for(i=0;i<10;i++)
   s3c2410_gpio_setpin(S3C2410_GPG11,1); //CE
  printk("WAIT FOR IRQ TO READY\n");
  i=0;
 while(i<2000&&s3c2410_gpio_getpin(S3C2410_GPH9))
 {//GET_IRQ()){//等待发送完成
  i++;
 }
 sta=SPI_Read(STATUS);  //读取状态寄存器的值   
 SPI_RW_Reg(WRITE_REG+STATUS,sta); //清除TX_DS或MAX_RT中断标志
 if(sta&MAX_TX)//达到最大重发次数
 {
  printk("MAX_TX reached!\n");
  SPI_RW_Reg(FLUSH_TX,0xff);//清除TX FIFO寄存器
  return MAX_TX;
 }
 else
 {
  printk("MAX_TX not reached yet!\n");
 }
 if(sta&TX_OK)//发送完成
 {
  printk("TX OK!\n");
  return TX_OK;
 }
 printk("TX FAILED!\n");
 return 0xff;//其他原因发送失败
}

/**********************************************
* 启动NRF24L01发送一次数据
* txbuf:待发送数据首地址
* 返回值:0,接收完成;其他,错误代码
**********************************************/
uchar RxPacket(uchar *rxbuf)
{
 uchar sta;               
 sta=SPI_Read(STATUS);  //读取状态寄存器的值     
 SPI_RW_Reg(WRITE_REG+STATUS,sta); //清除TX_DS或MAX_RT中断标志
 if(sta&RX_OK)//接收到数据
 {
  SPI_Read_Buf(RD_RX_PLOAD,rxbuf,RX_PLOAD_WIDTH);//读取数据
  SPI_RW_Reg(FLUSH_RX,0xff);//清除RX FIFO寄存器
  printk("Data received!\n");
  return 0;
 }   
 return 1;//没收到任何数据

/********************************************
* 该函数初始化NRF24L01到RX模式
* 设置RX地址,写RX数据宽度,选择RF频道,波特率和LNA HCURR
* 当CE变高后,即进入RX模式,并可以接收数据了 
*********************************************/   
void SET_RX_Mode(void)
{
 int i;
 GPIO_CONFIG_PINS(); //设置引脚
 for(i=0;i<10;i++)
   s3c2410_gpio_setpin(S3C2410_GPG11,0); //CE
   SPI_Write_Buf(WRITE_REG+RX_ADDR_P0,(uchar*)RX_ADDRESS,RX_ADR_WIDTH);//写RX节点地址
        SPI_RW_Reg(WRITE_REG+EN_AA,0x01);    //使能通道0的自动应答   
  SPI_RW_Reg(WRITE_REG+EN_RXADDR,0x01);//使能通道0的接收地址   
  SPI_RW_Reg(WRITE_REG+RF_CH,40);      //设置RF通信频率   
  SPI_RW_Reg(WRITE_REG+RX_PW_P0,RX_PLOAD_WIDTH);//选择通道0的有效数据宽度     
  SPI_RW_Reg(WRITE_REG+RF_SETUP,0x0f);//设置TX发射参数,0db增益,2Mbps,低噪声增益开启  
  SPI_RW_Reg(WRITE_REG+CONFIG, 0x0f);//配置基本工作模式的参数;PWR_UP,EN_CRC,16BIT_CRC,接收模式
  for(i=0;i<10;i++)
   s3c2410_gpio_setpin(S3C2410_GPG11,1); //CE
}  

/***********************************************    
* 该函数初始化NRF24L01到TX模式
* 设置TX地址,写TX数据宽度,设置RX自动应答的地址,
* 填充TX发送数据,选择RF频道,波特率和LNA HCURR
* PWR_UP,CRC使能
*当CE变高后,即进入RX模式,并可以接收数据了    
*CE为高大于10us,则启动发送.
***********************************************/ 
void SET_TX_Mode(void)
{
 int i;
 GPIO_CONFIG_PINS();//设置引脚
 for(i=0;i<10;i++)
   s3c2410_gpio_setpin(S3C2410_GPG11,0); //CE
   SPI_Write_Buf(WRITE_REG+TX_ADDR,(uchar*)TX_ADDRESS,TX_ADR_WIDTH);//写TX节点地址
  SPI_Write_Buf(WRITE_REG+RX_ADDR_P0,(uchar*)RX_ADDRESS,RX_ADR_WIDTH); //设置TX节点地址,主要为了使能ACK  
        SPI_RW_Reg(WRITE_REG+EN_AA,0x01);     //使能通道0的自动应答   
  SPI_RW_Reg(WRITE_REG+EN_RXADDR,0x01); //使能通道0的接收地址  
        SPI_RW_Reg(WRITE_REG+SETUP_RETR,0x1a);//设置自动重发间隔时间:500us + 86us;最大自动重发次数:10次
   SPI_RW_Reg(WRITE_REG+RF_CH,40);       //设置RF通道为40
  SPI_RW_Reg(WRITE_REG+RF_SETUP,0x0f);  //设置TX发射参数,0db增益,2Mbps,低噪声增益开启  
        SPI_RW_Reg(WRITE_REG+CONFIG,0x0e);    //配置基本工作模式的参数;PWR_UP,EN_CRC,16BIT_CRC,接收模式,开启所有中断
 SPI_RW_Reg(WRITE_REG+STATUS,0x0);
 for(i=0;i<10;i++)
  s3c2410_gpio_setpin(S3C2410_GPG11,1); //CE
}

/***********************************************
* 初始化24L01的IO口
************************************************/
void NRF24L01_Init(void)
{
 int i;
 printk("NRF24L01_Init\n");
 GPIO_CONFIG_PINS();//设置引脚
 //initial io
 for(i=0;i<5;i++)
 {
  s3c2410_gpio_setpin(S3C2410_GPG11,0); //CE
  s3c2410_gpio_setpin(S3C2410_GPB0,1); //CSN
  s3c2410_gpio_setpin(S3C2410_GPB4,0); //SCK
 }
 for(i=0;i<5;i++)
  s3c2410_gpio_setpin(S3C2410_GPG11,1); //CE
 SPI_RW_Reg(WRITE_REG + CONFIG, 0x0); // Set PWR_UP bit, enable CRC(2 bytes) & Prim:RX. RX_DR enabled..
 SPI_RW_Reg(WRITE_REG + EN_AA, 0x0);
 SPI_RW_Reg(WRITE_REG + EN_RXADDR, 0x0); // Enable Pipe0
 SPI_RW_Reg(WRITE_REG + SETUP_AW, 0x03); // Setup address width=5 bytes
 SPI_RW_Reg(WRITE_REG + SETUP_RETR, 0x0); // 500us + 86us, 10 retrans...
 SPI_RW_Reg(WRITE_REG + RF_CH, 0);
 SPI_RW_Reg(WRITE_REG + RF_SETUP, 0x0); // TX_PWR:0dBm, Datarate:1Mbps, LNA:HCURR
 SPI_RW_Reg(WRITE_REG + RX_PW_P0, 0x0);
 //SPI_Write_Buf(WRITE_REG + TX_ADDR, (uchar*)TX_ADDRESS, TX_ADR_WIDTH);
 //SPI_Write_Buf(WRITE_REG + RX_ADDR_P0, (uchar*)TX_ADDRESS, TX_ADR_WIDTH);
 for(i=0;i<10;i++)
  s3c2410_gpio_setpin(S3C2410_GPG11,1); //CE
}

/**********************************************
* 检测24L01是否存在
* 返回值:0,成功;1,失败 
***********************************************/
uchar NRF24L01_Check(void)
{
 uchar i; 
 uchar buf[5]={0XA5,0XA5,0XA5,0XA5,0XA5};
 GPIO_CONFIG_PINS();//设置引脚
 //SET_IRQ(1);
 SPI_Write_Buf(WRITE_REG+TX_ADDR,buf,5);//写入5个字节的地址. 
 SPI_Read_Buf(TX_ADDR,buf,5); //读出写入的地址 
 printk("buffer is: ");
 for(i=0;i<5;i++)
  printk("%X ",buf[i]);
 printk("\n");
 for(i=0;i<5;i++)if(buf[i]!=0XA5)break;           
 if(i!=5)return 1;//检测24L01错误 
 return 0;   //检测到24L01
}
/*********************************************************************************/

/***********************
* 底层驱动函数结束
************************/

/*********************************************************************************/


/***********************************
* 驱动设备函数
************************************/
static int nrf_open(struct inode *inode,struct file *filp)
{
 filp->private_data=nrf_devp;
 while(NRF24L01_Check())
 { //检测24L01是否存在
  printk("-----------------------------------\n");
  printk("NRF24L01 not detected!\n");
  printk("exiting...\n");
  printk("-----------------------------------\n");
 }
  printk("-----------------------------------\n");
  printk("NRF24L01 detected!\n");
  printk("NRF24L01 initiated!\n");
  printk("-----------------------------------\n");
  return 0;
}

static int nrf_release(struct inode *inode,struct file *filp)
{
 printk("-----------------------------\n");
 printk("NRF24L01 closed\n");
 printk("------------------------------\n");
 return 0;
}
//-----------------------------------------------------------------------------------------------------------
static ssize_t nrf_read(struct file *filp,char __user *buf,size_t count)
{
  int m;
  struct nrf_dev *dev=filp->private_data;
  SET_RX_Mode();
  m=0;
  while(m <20)
  {
  //printk("m*****************=%d\n",m);
   if(!RxPacket(dev->dataRx))
   { //接收一个包的数据
    for(m=0;m<RX_PLOAD_WIDTH;m++)
 //    printk("received: 0x%X \n",dev->dataRx[m]);
    count= copy_to_user(buf,dev->dataRx,RX_PLOAD_WIDTH);
       return RX_PLOAD_WIDTH;
   }
   m++;
  }

        return 0;
// return count;
}

static ssize_t nrf_write(struct file *filp,char __user *buf,size_t count)
{
 int i=0;
 struct nrf_dev *dev=filp->private_data;
  //获得设备结构体的指针
  copy_from_user(dev->dataTx,buf,TX_PLOAD_WIDTH);
  NRF24L01_Init();
  delay(100);
  SET_TX_Mode();
  delay(10);
  while(i<20 && TxPacket(dev->dataTx) != TX_OK)
  {
  i++;
  }
  if(i<20)
   printk("send succeed!\n");
  return 0;
}

/*********************************************************************************************/
static const struct file_operations nrf_fops=
{
 .owner=THIS_MODULE,
 .open=nrf_open,
 .release=nrf_release,
 .read=nrf_read,
 .write=nrf_write,
};

static void nrf_setup_cdev(struct nrf_dev *dev,int index)
{
 int err,devno=MKDEV(NRF_MAJOR,index);
  printk("-----------------------------------\n");
 cdev_init(&dev->cdev,&nrf_fops);  //初始化设备
 
 dev->cdev.owner=THIS_MODULE;
 dev->cdev.ops=&nrf_fops;
 err=cdev_add(&dev->cdev,devno,1);
 if(err)
  printk("S3C2440_NRF dev setup error\n");
 else
  printk("S3C2440_NRF dev setup succeed\n");
 printk("-----------------------------------\n");
}

static int __init nrf_init(void)
{
  int ret;
  dev_t devno=MKDEV(NRF_MAJOR,0);
  if(NRF_MAJOR)//申请设备号
    ret=register_chrdev_region(devno,1,NRF_NAME);
  else//动态申请设备号
  {
    ret=alloc_chrdev_region(&devno,0,1,NRF_NAME);
    NRF_MAJOR=MAJOR(devno);
  }
  if(ret<0)
  {
   printk("apply device failed!\n");
   return ret;
  }
  //动态申请设备结构体的内存
  nrf_devp=kmalloc(sizeof(struct nrf_dev),GFP_KERNEL);
  if(!nrf_devp)//申请失败
  {
   ret=-ENOMEM;
   printk("apply device memory failed!\n");
   goto fail_malloc;
  }
  memset(nrf_devp,0,sizeof(struct nrf_dev));
  //设备初始化
  nrf_setup_cdev(nrf_devp,0);
  return 0;
 fail_malloc:
  unregister_chrdev_region(devno,1);
  return ret;
}
static void __exit nrf_exit(void)
{
 unregister_chrdev(NRF_MAJOR,NRF_NAME);
 printk("S3C2440_NRF dev exit!\n");
}
/*******************************************************************************/
module_init(nrf_init);
module_exit(nrf_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("jingtao_deng");
MODULE_DESCRIPTION("NRF24L01 DRIVER FOR S3C2440");

 

 

 


/*
 * jingtao_deng
 * nrf24l01.h
*/

//GPB
#define GPBCON (unsigned long)ioremap(0x56000010,4)
#define GPBDAT (unsigned long)ioremap(0x56000014,4)
#define GPBUP  (unsigned long)ioremap(0x56000018,4)
//GPF
#define GPFCON (unsigned long)ioremap(0x56000050,4)
#define GPFDAT (unsigned long)ioremap(0x56000054,4)
#define GPFUP  (unsigned long)ioremap(0x56000058,4)
//GPG
#define GPGCON (unsigned long)ioremap(0x56000060,4)
#define GPGDAT (unsigned long)ioremap(0x56000064,4)
#define GPGUP  (unsigned long)ioremap(0x56000068,4)

#define uchar unsigned char
#define uint  unsigned int
#define ulong unsigned long

//相关命令的宏定义,原理参见《spi接口指令.jpg》
// SPI(nRF24L01) commands
#define READ_REG 0x00 // Define read command to register
#define WRITE_REG 0x20 // Define write command to register
#define RD_RX_PLOAD 0x61 // Define RX payload register address
#define WR_TX_PLOAD 0xA0 // Define TX payload register address
#define FLUSH_TX 0xE1 // Define flush TX register command
#define FLUSH_RX 0xE2 // Define flush RX register command
#define REUSE_TX_PL 0xE3 // Define reuse TX payload register command
#define NOP 0xFF // Define No Operation, might be used to read status register
//***************************************************//
// SPI(nRF24L01) registers(addresses)
#define CONFIG 0x00 // 'Config' register address
#define EN_AA 0x01 // 'Enable Auto Acknowledgment' register address
#define EN_RXADDR 0x02 // 'Enabled RX addresses' register address
#define SETUP_AW 0x03 // 'Setup address width' register address
#define SETUP_RETR 0x04 // 'Setup Auto. Retrans' register address
#define RF_CH 0x05 // 'RF channel' register address
#define RF_SETUP 0x06 // 'RF setup' register address
#define STATUS 0x07 // 'Status' register address
#define OBSERVE_TX 0x08 // 'Observe TX' register address
#define CD 0x09 // 'Carrier Detect' register address
#define RX_ADDR_P0 0x0A // 'RX address pipe0' register address
#define RX_ADDR_P1 0x0B // 'RX address pipe1' register address
#define RX_ADDR_P2 0x0C // 'RX address pipe2' register address
#define RX_ADDR_P3 0x0D // 'RX address pipe3' register address
#define RX_ADDR_P4 0x0E // 'RX address pipe4' register address
#define RX_ADDR_P5 0x0F // 'RX address pipe5' register address
#define TX_ADDR 0x10 // 'TX address' register address
#define RX_PW_P0 0x11 // 'RX payload width, pipe0' register address
#define RX_PW_P1 0x12 // 'RX payload width, pipe1' register address
#define RX_PW_P2 0x13 // 'RX payload width, pipe2' register address
#define RX_PW_P3 0x14 // 'RX payload width, pipe3' register address
#define RX_PW_P4 0x15 // 'RX payload width, pipe4' register address
#define RX_PW_P5 0x16 // 'RX payload width, pipe5' register address
#define FIFO_STATUS 0x17 // 'FIFO Status Register' register address

#define MAX_TX   0x10  //达到最大发送次数中断
#define TX_OK    0x20  //TX发送完成中断
#define RX_OK    0x40  //接收到数据中断

//24L01发送接收数据宽度定义
#define TX_ADR_WIDTH    5   //5字节的地址宽度
#define RX_ADR_WIDTH    5   //5字节的地址宽度
#define TX_PLOAD_WIDTH  32  //2字节的用户数据宽度
#define RX_PLOAD_WIDTH  32  //2字节的用户数据宽度

//functions
void delay(int n);

void SET_CSN(uchar uc);  //设置CSN位
void SET_CE(uchar uc);  //设置CE位
void SET_SCK(uchar uc);  //设置SCK位
void SET_MOSI(uchar uc);  //设置MOSI位
void SET_IRQ(uchar uc);  //设置IRQ位
uchar GET_MISO(void);  //从MISO获取数据
uchar GET_IRQ(void);   //从IRQ获取数据

uchar SPI_RW(uchar byte);
uchar SPI_RW_Reg(uchar reg, uchar value);
uchar SPI_Read(uchar reg);
uchar SPI_Read_Buf(uchar reg, uchar *pBuf, uchar bytes);
uchar SPI_Write_Buf(uchar reg, uchar *pBuf, uchar bytes);

uchar TxPacket(uchar *rxbuf);  //接收一个包的数据
uchar TxPacket(uchar *txbuf);  //发送一个包的数据 
void RX_Mode(void);
void TX_Mode(void);

uchar NRF24L01_Check(void);  //检查24L01是否存在

 

评论 3
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值