简易无接触温度测量与身份识别装置(2020年全国大学生电子设计竞赛F题)

一、具体题目

本装置是2020年全国大学生电子设计竞赛山东赛区的F题,具体要求可能同其他省份有一些差别,但基本相似。

1. 任务说明

题目要求设计并制作一个简易无接触温度测量与身份识别装置,该装置包括无接触温度测 量模块、身份识别模块、处理器模块和电源等,装置组成框图如下图所示。装置中无接触温度测量模块可以无接触测量人体体温和容器中液态水的温度。测试时,应有光标指示被测点,当被测温度超过设定值时,应有报警功能;身份识别模块负责辨别被测人身份、是否符合防疫要求(如佩戴口罩)等。
简易体温测量与身份识别装置组成图

2. 任务要求

  1. 非接触温度测量功能,测试距离 1cm~4cm;测量误差绝对值小于等于 2℃ 。
  2. 温度测量范围:28℃ ~48℃,并具有温度超标报警功能 。报警温度阈值在
    30℃ ~46℃范围内可设置,报警方式自定。
  3. 身份识别功能:被测人身份识别和身份不符报警功能。
  4. 被测人是否符合防疫要求(如佩戴口罩)判别功能。
  5. 现场被测人身份特征学习与身份识别功能,学习时间小于等于10 分钟。
  6. 其他

3. 任务说明

(1) 该装置不能采用市售产品,否则无分。作品不能使用 PC 机,且测试中不能借
助网络资源。
(2) 温度测量项。该装置的测量温度范围将超出人体温度范围,测试对象为现场人 员和装在容器中的液态水,并采用相应标准温度测量设备作为测量误差对比装
置。温度测量功能评测时,测量误差以作品测量数据与标准温度测量装置测量
数据之差为准。作品测试时,参赛学生可自带容器和标准温度测量设备。
(3) 距离测量项。选作品测量误差对应的测试点,测量起始距离在 1cm~4cm 之间
任选,在保持其误差水平的基础上,距离越远越好。
(4) 身份识别功能项。识别对象为参赛队 3 名队员,识别方法采用面部识别,识别
结果可自选方式表示。
(5) 要求(4)可仅判断被测人是否符合佩戴口罩的防疫要求。
(6) 要求(5)在测量现场的准备阶段完成学习过程,学习对象为现场的工作人员,
要求经过现场学习,能准确识别学习对象的身份。

二、具体方案

本装置以STM32F103ZET6为主控芯片,配合Open MV(摄像头)、MLX90614(红外测温模块)以及蜂鸣器、LED、矩阵键盘、激光头等完成了基本要求。利用ISD1820(语音模块)实现了语音提示功能并且能够实现红外远程遥控。后期把矩阵键盘换成了触摸屏实现!

1. 模块简介

(1)Open MV

Open MV简单的来说,它是一个可编程的摄像头,通过MicroPython语言,可以实现你的逻辑。而且摄像头本身内置了一些图像处理算法,很容易使用。此外星瞳科技官网有许多上手教程,开发时可参考,附上网址Open MV上手教程

本次我们由于资金问题使用的是Open MV4 H7,而没有采用Open MV4Plus,Open MV4 H7 Plus相比于前者增加了神经网络的功能,可以通过神经网络来实现口罩识别,具体教程也在星瞳科技上手教程里有。

(2)MLX90614红外测温模块

MLX90614是由Melexis公司生产的一种红外温度计,波长范围为5.5~14 um。它是由红外热电堆传感器MLX81101和信号处理专用芯片MLX90302共同集成,可实现高精度和高分辨率的温度采集测量辨析度可达0.02 ℃。MLX90614通过IR传感器测量目标温度,并由内部状态机控制物体温度和环境温度的测量和计算,进行温度处理后可以将结果通过二线 SMBUS兼容协议接口或IO位PWM输出模式实现数据传输,操作简单,准确度高。

关于红外测温的原理,任何物体都存在热辐射,其发射的红外辐射强度能够反应物体自身的温度,红外测温技术就是根据捕捉到的物体红外辐射能量进行物体的温度测量的。我们当时为了增加测量距离购买了测量距离较远的也就是下图里的第三个,当时我们也查了一些相关资料,据说利用菲涅尔透镜可以增加测温距离,具体原理我已经记不清楚了,所以就不乱说了,想了解的大家可以自行搜索。

MLX90614图片

(3)矩阵键盘

矩阵键盘是单片机外部设备中所使用的排布类似于矩阵的键盘组,按键设置在行、列线交点上,行、列线分别连接到按键开关的两端,行线通过上拉电阻接到加5V电源上,无按键按下时,行线处于高电平的状态,而当有按键按下时,行线电平与此行线相连的列线电平决定。

当时我们用的是4*4的矩阵键盘,需要用到8个I/O口来进行控制。这里举个例子,比如我用PA0~PA7这8个I/O口来控制矩阵键盘,低四位接控制列线电平,高四位接行线的四个引脚,工作时给四条列线赋低电平,然后检测行线电平状态,如果没有按键按下,则所有行线应该都处于高电平状态,一旦有一个按键按下,则该按键所在的行线一定会变为低电平,这个时候就可以确定按下按键所在的行,接下来分别给四条列线循环赋低电平,继续检测行线状态,如果某一列线赋为低电平时某一行线也为低电平,则按下的就是它们交点处的按键。当然每次检测都要有消抖的过程。

由于本人当时能力有限,对于利用矩阵键盘来设置阈值的程序部分的写的很垃圾,但是基本要求还是可以满足的。

(4)显示模块

我用的是2.8寸电容屏,可以触摸,就是下图这种,某宝链接找不到了具体的开发资料以及使用的注意事项后续还会发帖。2.8寸电容屏

(5)红外遥控

通用红外遥控系统由发射和接收两大部分组成,应用编/解码专用集成电路芯片来进行控制操作。发射部分包括键盘矩阵、编码调制、LED红外发送器;接收部分包括光、电转换放大器、解调、解码电路。

目前有的编码方式由PWM(脉冲宽度调制)和PPM(脉冲位置调制),NEC是脉冲宽度调制。PWM以发射红外载波的占空比代表“0”和“1”。PPM以发射载波的位置表示“0”和“1”。

2. 具体思路

对于测温部分由单片机配合红外测温模块可以很容易实现,无非就是误差问题处理起来比较复杂,当时也是因为能力有限,再加上比赛时间紧迫,没有多次测量用MATLAB来拟合误差函数,关于如何利用MATLAB来拟合误差函数,这里附上另外一个大佬的帖子MATLAB中的polyfit函数的使用方法

对于被测人身份识别和身份不符报警功能,我当时参考了星瞳科技上手教程里分辨不同人脸的程序,根据题目要求,赛前我们需要先把三名参赛选手的人脸信息录入,录入信息就是拍摄60张当前摄像头里的人脸照片然后存在单独的文件夹中,用于人脸识别时进行对比。而身份识别就是拍摄一张当前摄像头中的人脸照片,然后与每个文件夹中的照片进行特征对比,最终返回一个差异度最低的人的编号,但是这就有一个问题,就是无论摄像头中的人是否已经入库,最终都会返回一个与其差异度最低的人,为了解决这个问题我们多次测量后设置了一个阈值,当差异度小于7200的时候才会返回被测人身份编号,否则显示未入库并且报警。这个7200是我们根据当时的环境确定的,也可以自行根据实际情况调节。值得一提的是,Open MV与单片机我们采用的是串口通信,而没有采用任何通信模块,其实也完全可以用两个蓝牙或者2.4G通信模块等等来实现二者的通信。

对于远程控制我们也是当作拓展功能加入的,也是由于当时只会红外遥控,就采用了这种方法,但是也可以用蓝牙或者wifi模块来控制。

三、功能实现

1. 无接触温度测量

以下是MXL90614.c文件中的部分程序

// An highlighted block
/*================================================================================================
//  @函数名称:
//  @函数功能:读MLX90614的RAM中内容
//  @函数说明:主要读取三个,环境温度,物体温度1,物体温度2
//				器件从地址可以通过向EEPROM的SMBus地址0x0E中写入来进行设定。
//------------------------------------------------------------------------------------------------
//  @参数值:	saddr:从机地址,7位地址,任何MLX90614都会对0x00地址作出反应
//				cmd:存放温度的寄存器地址
//  @返回值:	Data:读取出来的数值。SA(write) - Command - SA(read) - LSByte - MSByte - PEC
//==============================================================================================*/
static uint16_t mlx90614_read_ram(uint8_t saddr,uint8_t cmd)
{
	uint16_t Data;
	uint8_t DataL; //接收数据低字节
	uint8_t DataH; //接收数据高字节
	uint8_t PEC;
	uint8_t retry = 10; //失败重复次数
	uint8_t s_ack = 0;
	uint8_t Pecreg; //计算的PEC值
	uint8_t buf[6]; //存储已接收数据的缓存
	MLX90614_IIC_SCL=0;	
	while(retry--)
	{
		mlx90614_iic_start(); //发送起始位
		s_ack = mlx90614_iic_write_byte((saddr<<1)|WR); //发送从机地址和Wr位
		if(s_ack == ACK_SUCCESS)
		{
			s_ack = 0;
			s_ack = mlx90614_iic_write_byte(RAM|cmd);
			//发送命令,8位,RAM表示对RAM操作,cmd表示操作RAM的地址
			if(s_ack == ACK_SUCCESS)
			{
				s_ack = 0;
				mlx90614_iic_start(); //重新发送起始位
				s_ack = mlx90614_iic_write_byte((saddr<<1)+1); //发送从机地址和Rd位
				if(s_ack == ACK_SUCCESS)
				{
					s_ack = 0;
					DataL = mlx90614_iic_read_byte(1); //读数据低字节
					DataH = mlx90614_iic_read_byte(1); //读数据高字节
					PEC = mlx90614_iic_read_byte(1); //读数据PEC字节
					// DataL=RX_byte(0); //
					// DataH=RX_byte(0); //
					// PEC=RX_byte(1);
					mlx90614_iic_stop(); //发送停止位
					buf[5]=(saddr<<1);
					buf[4]=EEPROM|cmd;
					buf[3]=(saddr<<1)|RD;
					buf[2]=DataL;
					buf[1]=DataH;
					buf[0]=0;
					Pecreg=pec_cal(buf,6); //调用计算 PEC 的函数
					if(Pecreg == PEC)
					{
						break; //退出循环
					}
				}
				else goto stop_rr;
			}
			else goto stop_rr;
		}
		else goto stop_rr;
		stop_rr:
		mlx90614_iic_stop(); //发送停止位,芯片接收失败
	}
	PEC = PEC+1;
	Data = (DataH<<8) + DataL;
	return Data;
}
/*================================================================================================
//  @函数名称:
//  @函数功能:读取目标温度
//  @函数说明:
//------------------------------------------------------------------------------------------------
//  @参数值:	无
//  @返回值:	读取的温度值
//-----------------------------------------------------------------------------------------------
//==============================================================================================*/
float mlx90614_read_temp(void)
{
	return (float)mlx90614_read_ram(0x00,OBJ1TEMPADDR)*0.02-273.15;
}

测量液体温度时误差满足要求就没有再进行误差矫正

void water()
{
	temp=mlx90614_read_temp();
	sprintf(value_Tm,"%0.2f", temp);  //浮点型转换成字符串	
	LCD_ShowString(180,14,200,24,24,(uint8_t *)value_Tm);
}

但是在测量人体温度时误差不满足要求,所以增加了误差矫正,正如上面所说,因为当时能力有限并没有拟合出一个多项式函数,直接在原有温度上加了2℃,但是在1.5m内测量误差还是满足要求的。具体函数如下:

void people()
{
	temp=mlx90614_read_temp()+2.00;
	sprintf(value_Tm,"%0.2f", temp);  //浮点型转换成字符串	
	LCD_ShowString(180,14,200,24,24,(uint8_t *)value_Tm);
}

2. 人脸录入

该段程序直接采用的星瞳科技例程,无非改动了图片拍摄次数以及拍摄间隔,代码如下:

# Snapshot Example
#
# Note: You will need an SD card to run this example.
#
# You can use your OpenMV Cam to save image files.

import sensor, image, pyb

RED_LED_PIN = 1
BLUE_LED_PIN = 3

sensor.reset() # Initialize the camera sensor.
sensor.set_pixformat(sensor.GRAYSCALE) # or sensor.GRAYSCALE
sensor.set_framesize(sensor.B128X128) # or sensor.QQVGA (or others)
sensor.set_windowing((92,112))
sensor.skip_frames(10) # Let new settings take affect.
sensor.skip_frames(time = 2000)

num = 1 #设置被拍摄者序号,第一个人的图片保存到s1文件夹,第二个人的图片保存到s2文件夹,以此类推。每次更换拍摄者时,修改num值。

n = 60 #设置每个人拍摄图片数量。

#连续拍摄n张照片,每间隔3s拍摄一次。
while(n):
    #红灯亮
    pyb.LED(RED_LED_PIN).on()
    sensor.skip_frames(time = 200) # Give the user time to get ready.等待20ms,准备一下表情。

    #红灯灭,蓝灯亮
    pyb.LED(RED_LED_PIN).off()
    pyb.LED(BLUE_LED_PIN).on()

    #保存截取到的图片到SD卡
    print(n)
    sensor.snapshot().save("singtown/s%s/%s.pgm" % (num, n) ) # or "example.bmp" (or others)

    n -= 1

    pyb.LED(BLUE_LED_PIN).off()
    print("Done! Reset the camera to see the saved image.")

3. 身份识别

该部分代码也是基于星瞳科技官网代码进行了部分改动,无非在输出时加了一个差异度判断,只有差异度小于7200的才会被返回。

# Face recognition with LBP descriptors.
# See Timo Ahonen's "Face Recognition with Local Binary Patterns".
#
# Before running the example:
# 1) Download the AT&T faces database http://www.cl.cam.ac.uk/Research/DTG/attarchive/pub/data/att_faces.zip
# 2) Exract and copy the orl_faces directory to the SD card root.


import sensor, time, image, pyb  

sensor.reset() # Initialize the camera sensor.
sensor.set_pixformat(sensor.GRAYSCALE) # or sensor.GRAYSCALE
sensor.set_framesize(sensor.B128X128) # or sensor.QQVGA (or others)
sensor.set_windowing((92,112))
sensor.skip_frames(10) # Let new settings take affect.
sensor.skip_frames(time = 5000) #等待5s



#SUB = "s1"
NUM_SUBJECTS = 6 #图像库中不同人数,一共6人
NUM_SUBJECTS_IMGS = 20 #每人有20张样本图片

# 拍摄当前人脸。
img = sensor.snapshot()
#img = image.Image("singtown/%s/1.pgm"%(SUB))
d0 = img.find_lbp((0, 0, img.width(), img.height()))
#d0为当前人脸的lbp特征
img = None
pmin = 9
num=0

def min(pmin, a, s):
    global num
    if a<pmin:
        pmin=a
        num=s
    return pmin

for s in range(1, NUM_SUBJECTS+1):
    dist = 0
    for i in range(2, NUM_SUBJECTS_IMGS+1):
        img = image.Image("singtown/s%d/%d.pgm"%(s, i))
        d1 = img.find_lbp((0, 0, img.width(), img.height()))
        #d1为第s文件夹中的第i张图片的lbp特征
        dist += image.match_descriptor(d0, d1)#计算d0 d1即样本图像与被检测人脸的特征差异度。
    print("Average dist for subject %d: %d"%(s, dist/NUM_SUBJECTS_IMGS))
    pmin = min(pmin, dist/NUM_SUBJECTS_IMGS, s)#特征差异度越小,被检测人脸与此样本更相似更匹配。
    print(pmin)
    if(pmin>7200):
        num=91
output_str="[%.0f]" %num
print(num) # num为当前最匹配的人的编号。
uart.write(output_str+'\r\n')

4. 串口通信

单片机的串口通信程序这里就不再过多赘述了,主要描述一下Open MV的串口通信,Open MV引脚P4是TXD,P5是RXD,分别接单片机的PA10(RXD)和PA9(TXD),二者再共地就可以实现信息传输了,Open MV串口发送代码如下,有关详细介绍可以去星瞳科技官网自行学习。

import time
from pyb import UART

uart = UART(3, 19200)

while(True):
    uart.write("Hello World!\r")
    time.sleep_ms(1000)

5. 红外遥控

以下是hwjs.c文件中的程序,因为4*4的矩阵键盘我们只用到了13个按键,所以只写到13。主函数中只需要编写相应的按键服务函数,根据返回键值来实现相应功能即可。至于其中的红外按键码本人是事先用串口的方式一个一个测出来的,不同的遥控器对应的码可能不同。

u32 hw_jsm;	  //定义一个32位数据变量,保存接收码
u8  hw_jsbz;  //定义一个8位数据的变量,用于指示接收标志

/*******************************************************************************
* 函 数 名         : Hwjs_Init
* 函数功能		   : 红外端口初始化函数	  时钟端口及外部中断初始化 
* 输    入         : 无
* 输    出         : 无
*******************************************************************************/
void Hwjs_Init()
{
	GPIO_InitTypeDef GPIO_InitStructure;
	EXTI_InitTypeDef EXTI_InitStructure;
	NVIC_InitTypeDef NVIC_InitStructure;

	/* 开启GPIO时钟及管脚复用时钟 */
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOE|RCC_APB2Periph_AFIO,ENABLE);

	GPIO_InitStructure.GPIO_Pin=GPIO_Pin_0;//红外接收
	GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IPU;//上拉输入模式,来了一个低电平之后就会产生下降沿,触发中断
	GPIO_Init(GPIOE,&GPIO_InitStructure);
	
	GPIO_EXTILineConfig(GPIO_PortSourceGPIOE, GPIO_PinSource0); //选择GPIO管脚用作外部中断线路
	EXTI_ClearITPendingBit(EXTI_Line0);//清除中断标志
	
	/* 设置外部中断的模式 */ 
	EXTI_InitStructure.EXTI_Line=EXTI_Line0;
	EXTI_InitStructure.EXTI_Mode=EXTI_Mode_Interrupt;
	EXTI_InitStructure.EXTI_Trigger=EXTI_Trigger_Falling;
	EXTI_InitStructure.EXTI_LineCmd=ENABLE;
	EXTI_Init(&EXTI_InitStructure); 

	/* 设置NVIC参数 */
	NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn;   //打开全局中断
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; //抢占优先级为0
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; 	 //响应优先级为1
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;   //使能
	NVIC_Init(&NVIC_InitStructure);

}


/*******************************************************************************
* 函 数 名         : HW_jssj
* 函数功能		   : 高电平持续时间,将记录的时间保存在t中返回,其中一次大约20us 
* 输    入         : 无
* 输    出         : t
*******************************************************************************/
u8 HW_jssj()
{
	u8 t=0;
	while(GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_0)==1)//高电平
	{
		t++;
		delay_us(20);
		if(t>=250) return t;//超时溢出
	}
	return t;
}


void EXTI0_IRQHandler(void)	  //红外遥控外部中断
{
	u8 Tim=0,Ok=0,Data,Num=0;

   while(1)
   {
	   	if(GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_0)==1)
		{
			 Tim=HW_jssj();//获得此次高电平时间

			 if(Tim>=250) break;//不是有用的信号

			 if(Tim>=200 && Tim<250)
			 {
			 	Ok=1;//收到起始信号
			 }
			 else if(Tim>=60 && Tim<90)
			 {
			 	Data=1;//收到数据 1
			 }
			 else if(Tim>=10 && Tim<50)
			 {
			 	Data=0;//收到数据 0
			 }

			 if(Ok==1)
			 {
			 	hw_jsm<<=1;
				hw_jsm+=Data;

				if(Num>=32)
				{
					hw_jsbz=1;
				  	break;
				}
			 }

			 Num++;
		}
   }

   EXTI_ClearITPendingBit(EXTI_Line0);	
}

/*******************************************************************************
* 函 数 名         : HW_key
* 函数功能		   : 通过获取 hw_jsm判断红外键盘哪个按键按下
* 输    入         : 无
* 输    出         : 
*******************************************************************************/

u8 HW_key()
{
	if(hw_jsm==0x00ff6897)
	{
		return 0;
	}
	else if(hw_jsm==0x00ff30cf)
	{
		return 1;
	}
	else if(hw_jsm==0x00ff18e7)
	{
		return 2;
	}
	else if(hw_jsm==0x00ff7a85)
	{
		return 3;
	}
	else if(hw_jsm==0xff10ef)
	{
		return 4;
	}
	else if(hw_jsm==0x00ff38c7)
	{
		return 5;
	}
	else if(hw_jsm==0x00ff5aa5)
	{
		return 6;
	}
	else if(hw_jsm==0x00ff42bd)
	{
		return 7;
	}
	else if(hw_jsm==0xff4ab5)
	{
		return 8;
	}
	else if(hw_jsm==0xff52ad)
	{
		return 9;
	}
	else if(hw_jsm==0x00ffa25d)
	{
		return 10;
	}
	else if(hw_jsm==0x00ff02fd)
	{
		return 11;
	}
	else if(hw_jsm==0x00ffc23d)
	{
		return 12;
	}
	return 13;
}

6. 触摸模式下的按键GUI

本人后来将矩阵键盘改成了触摸屏,因为需要设置阈值就自己画了一个键盘的GUI,具体程序如下:
需要注意的是,关于屏幕的扫描模式选用的是从上到下,从右到左的模式。

void tap_keyboard_gui()
{
	LCD_Fill(10,50,310,110,GRAY);
	LCD_Fill(10,190,85,230,RED);
	LCD_Fill(235,190,310,230,GREEN);
	LCD_Fill(10,110,310,190,YELLOW);
	LCD_Fill(85,190,235,230,YELLOW);
	
	LCD_DrawRectangle(10,50,310,230);
	LCD_DrawLine(10,110,310,110);
	LCD_DrawLine(10,150,310,150);
	LCD_DrawLine(10,190,310,190);
	
	LCD_DrawLine(85,110,85,230);
	LCD_DrawLine(160,110,160,230);
	LCD_DrawLine(235,110,235,230);
	
	
	BACK_COLOR=YELLOW;
	LCD_ShowString(40,120,200,24,24,"1");
	LCD_ShowString(115,120,200,24,24,"2");
	LCD_ShowString(190,120,200,24,24,"3");
	LCD_ShowString(265,120,200,24,24,"4");
	
	LCD_ShowString(40,160,200,24,24,"5");
	LCD_ShowString(115,160,200,24,24,"6");
	LCD_ShowString(190,160,200,24,24,"7");
	LCD_ShowString(265,160,200,24,24,"8");
	
	
	BACK_COLOR=RED;
	LCD_ShowString(15,195,200,24,24,"clear");
	BACK_COLOR=YELLOW;
	LCD_ShowString(115,200,200,24,24,"9");
	LCD_ShowString(190,200,200,24,24,"0");
	BACK_COLOR=GREEN;
	LCD_ShowString(240,195,200,24,24,"sure");
	BACK_COLOR=GRAY;
	LCD_ShowString(127,65,200,24,24,".");
}

GUI最后长这样,因为之前的图找不到了,就找了另一个项目里的图片,但是界面是一样的,个人感觉还可以哈哈哈
触摸屏按键GUI

四、成品展示

下面是当时参赛的成品图,还拍了一个小小的视频,剪辑技术比较烂,大家凑合着看吧。
成品图

测温+口罩检测演示视频

最后是附上程序下载地址简易无接触温度测量与身份识别装置单片机与摄像头程序下载

五、总结

本人目前已经是大四下学期,即将面临毕业,所以也就没有参加这些比赛的机会了,想起来写博客也是因为目前在找工作,想借着这个机会好好梳理一下自己大学期间做过的项目,也可以留个纪念。接下来是我想给以后想参加比赛的同学的一些建议:

(1)一定要注重前期准备!对于所有可能用到的模块或者资料一定提前整理好,比赛时可以直接用。
(2) 注重积累以及程序的规范性。无论是平时还是比赛,把自己接触到的一些模块或者算法程序都整理成例程,而且一定要注重规范性!必要的话可以写一个相关文档。
(3) 多多关注稳定性,不要只考虑实现功能即可,要考虑系统的稳定性!
(4) 一定注意分析题目,慎重选题。

本人也自己整理了一些常用的模块程序,就贴在下面了,后续大概会写一些其他的贴子,有需要的可以留言。
模块程序

  • 28
    点赞
  • 161
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 98
    评论
评论 98
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

二土电子

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

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

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

打赏作者

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

抵扣说明:

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

余额充值