Arduino ESP8266+RC522+阿里云 物联网入户控制RFID门禁系统

news2024/11/17 6:27:41

前言

根据项目结课报告改编而成,可能更适合作为一份文档而不是一篇记录类型的博客,没有留存接线图和运行图片,感到抱歉。

使用的板子是YwRobot的ESP8266板子,使用Arduino IDE开发,用到了舵机、按钮、人体感应传感器、射频模块等器件。另外使用了第三方代码改编实现了阿里云物联网平台的远程控制监控功能,使用aliyun iot studio进行开发。代码最后由于时间原因没有拆成多个文件,不过没超过500行问题也不算大XD。

使用这套设备的小伙伴可以尝试使用后面我发的源码中的引脚定义进行接线,可以避免不少问题(例如上传出现点和横线的组合然后报错),其中LED灯使用了板载LED BUILTIN_LED,在Arduino IDE中也要将其设置为0号;有一个按钮接在了唯一一个模拟IO A0上,可以看后面的代码。

对于RC522模块,可以参考这篇文章。接线就按照里面图的GPIOxxx,对应xxx接口来接就好了。

nodeMCU(ESP8266)和RC522的接线图_51CTO博客_nodemcu和esp8266是什么关系

需要完整源码的,可以在评论区留言。因为还涉及到与阿里云通信的库文件和代码,就不在博客中贴出了。

项目要求:

① 自动控制时,人体感应模块检测到有人、或者声音传感器检测到有声音,则打开门口的灯。手动控制时,不管传感器是否检测到人或声音,可以直接开关灯。

  ② 刷RFID卡开关门,信息正确则打开房门,信息不正确不开门。

  (1)可以通过网页进行远程监控。

  (2)远程端分别设置:

  ①本地控制/远程控制切换开关;

  ②手动控制/自动控制切换开关;

  ③远程手动控制时使用的设备总启动开关和总停止开关;设备端设置:本地手动控制时使用的设备总启动开关和总停止开关。

  设备端和远程端均需要直观显示当前设备的运行状况。本地控制时,远程端仅能进行监视、不能控制。

  本地控制分为手动控制和自动控制两种模式,当系统处于本地自动控制模式时,设备根据各传感器测量值和系统时间自动工作;

  当系统处于本地手动控制模式时,使用设备的手动输入元件和系统时间控制设备的动作。远程控制时,仅远程手动控制起作用、无远程自动控制模式。

  远程手动控制时,可以直观监视设备的运行状况,也可以通过网页遥控本地设备的动作。

设计方案:

本项目是一个物联网带照明RFID门禁系统,使用Arduino及相关开发板、元器件开发实现。

本项目主要由以下几个模块构成:

模式切换及运行控制模块、照明模块、开锁模块、物联网模块

  • 模式切换及运行控制模块

该系统设计了自动运行模式和手动运行模式,可以通过在本地端和远程端切换两种运行模式。

  • 照明模块

一个人体检测传感器和一个LED灯,经多次研究考虑后决定使用开发版内置LED。

  • 开锁模块

由RFID读取部分和舵机部分组成,分别对应门禁刷卡器和门。连接这两个部分的是开门验证程序,并额外增加了密码开门程序。

  • 物联网模块

用于实现该系统的远程控制,依托于阿里云物联网平台实现。

模块及对应的元器件(对应的软件见后面编程实现部分)

模块名称

对应的物理元件

模式切换及运行控制模块

开发板、按钮x1

照明模块

板内置LED、按钮x1、人体感应传感器

开锁模块

舵机、RFID读卡器

物联网模块

开发板(Wi-Fi芯片)

功能一览:

完成了所有目标要求,并额外实现了远程密码开锁功能,设计了精美的物联网控制UI界面。所有的功能均可正常使用并即时在物联网平台更新,远程操控功能正常与手动控制无冲突。

  1. 能读取指定的RFID卡并通过验证操纵舵机开门
  2. 在自动模式下人体感应传感器检测到物体运动将会打开灯,在手动模式下可以通过按钮/远程来开关灯(提醒传感器有一段时间时延)
  3. 可以通过按钮/远程控制自动和手动模式,在远程控制任何功能将会强制切换到手动模式,直到用户在本地通过按钮切换成自动模式
  4. 在物联网平台可以看到实时的数据:控制状态(自动还是手动),是否有人,灯的开关,卡的合法性(门的开启和关闭),操控:控制状态,开关灯,门的开启(密码)
  5. 额外增加了在远程端设置密码并使用密码来开门的功能

编程思路及关键代码说明:

编程实现(软件部分)是这套项目的核心也是重点,在项目的开发过程中大部分时间都用于此部分。在开发中使用了函数式编程的思想,在源码以及接下来关键功能的代码说明中可以看到功能的实现是函数化的。

Arduino开发语言在很大程度上是与C/C++兼容的,除了一些Arduino的内置函数,以及在验证门卡和使用密码开门时使用了Arduino封装的String()类型和在使用RFID-RC522射频模块读取卡片时使用了类和对象一些面向对象C++编程方法[https://github.com/miguelbalboa/rfid]外,本项目的代码可以被学过基础C语言的读者所读懂。

可以在Arduino官方文档[https://www.arduino.cc/reference/en/]找到相对应的Arduino语言特性。

本项目的代码本地变量名和函数名使用小驼峰命名法。

下面将按照模块进行说明,并指出一些容易出错的地方及其解决方案。

模式切换及运行控制模块

使用一个状态变量MODE来标识处于自动运行模式还是手动运行模式,在切换时翻转这个状态变量(二进制),并在需要分模式运行的功能运行前读取状态变量来选择提供的功能。

模式切换的代码是简单的,重要的是在这里引入了一段按钮防抖代码,用于消除机械开关的抖动。如果没有这段代码,按钮按下后你不能确定你会得到哪一个状态(结果完全是随机的,由抖动次数决定)。在下面的灯开关按钮中也需要用到。

在这里讲解一下防抖的思路:

在物理层面上避免机械开关的抖动是复杂的,毕竟没有专门设计消抖电路。发现问题后决定通过软件层面消除抖动。通过变量currentBtnX记录当前按钮的状态,并与按钮在上一时刻的状态lastBtnX进行对比,如果按钮的状态发生了变化,怎么确定是由于抖动引起的还是用户的实际操作?我们记录上一次由用户操作的按钮按下时间lastDebounceTimeX,通过millis()获取当前时间计算出两次操作之间的时间差,如果这个时间差小于一个较小的时间,可以确定是由于抖动产生的,我们只需处理时间差大于设定时间的操作,并更新按钮按下时间。

事实上Arduino也有第三方库来实现防抖,但基于种种考虑本项目并没有采用。

1.	void modeSwitch()  
2.	{  
3.	  currentBtn1 = digitalRead(modeSwitchBtn);  
4.	  if (lastBtn1 != currentBtn1) //模式切换按钮被按下,切换模式  
5.	  {  
6.	    if (millis() - lastDebounceTime1 > debounceDelay)  
7.	    {  
8.	      if (currentBtn1)  
9.	      {  
10.	        //Serial.println("ModeSwitchButton Pressed");  
11.	        MODE ^= 1;  
12.	      }  
13.	      lastDebounceTime1 = millis();  
14.	    }  
15.	  }  
16.	}  

照明模块

使用一个变量Detected来标识人体感应传感器是否感应到人体,一个变量ledStatus来标记灯的开关,以进一步写入高低电平来控制灯的亮灭。

1.	void ledControl()  
2.	{  
3.	  if (MODE) //手动控制  
4.	  {  
5.	    //Serial.printf("AnalogRead LEDBTN %d\n",analogRead(ledBtn));  
6.	    currentBtn2 = (analogRead(ledBtn) == 1024) ? 1 : 0;  
7.	    if (currentBtn2 != lastBtn2)  
8.	    {  
9.	      if (millis() - lastDebounceTime2 > debounceDelay)  
10.	      {  
11.	        if (currentBtn2)  
12.	        {  
13.	          Serial.println("LED Control Button pressed");  
14.	          ledStatus ^= 1;  
15.	          remoteLED = 0;  
16.	        }  
17.	        lastDebounceTime2 = millis();  
18.	      }  
19.	    }  
20.	    if (!remoteLED)  
21.	      digitalWrite(LED, !ledStatus);  
22.	  }  
23.	  else  
24.	    digitalWrite(LED, !Detected); //自动模式直接根据传感器返回的信号控制灯 
25.	}  

在这段代码中灯开关按钮读取代码由于该按钮接的是模拟引脚,需要将模拟数值(0-1024)转换成0和1来使用,经过实验发现按钮按下时返回1024,故将1024映射成数字量1。

在这里说明一下remoteLED变量,这是由于远程控制的数据发送到本地时是在获取数据的函数void mqtt_callback(char *topic, byte *payload, unsigned int length)中直接控制的,这是为了更少的延时,但也造成了一些设计上的瑕疵,或许有更好的方法来解决,但你只需要知道这是一个bug patch。当然这没有造成任何功能上的影响,在按钮按下时该变量复位,只是降低了一点代码的可读性和设计美学。

另外由于控制的是板内置LED,高电平灭,低电平亮,这点需要注意。

      void mqtt_callback(char *topic, byte *payload, unsigned int length)中控制灯的部分

1.	if (params_LightSwitch == 0)  
2.	{  
3.	  Serial.println("led off");  
4.	  digitalWrite(LED, 1);  
5.	  remoteLED = 1;  
6.	}  
7.	else if (params_LightSwitch == 1)  
8.	{  
9.	  Serial.println("led on");  
10.	  digitalWrite(LED, 0);  
11.	  remoteLED = 1;  
12.	}  

开锁模块

两种开锁方式,远程密码开锁和手动密码开锁。合法的密码legalPwd是在远程端首次操控前需要设置并保存到本地的,合法的卡UIDlegalID是在程序编写时根据读取结果设定的。通过变量cardvalidity来监控门的开启与否,而doorOpen变量是为了解决门的延迟3秒函数执行后才运行aliyunUpload()上报数据而增加的一个强制上传标识变量。在执行完开门操作后要讲密码或卡UID清空。

1.	void accessControl()  
2.	{  
3.	  readCard();  
4.	  if (readID == legalID || legalPwd == inputPwd)//两种方式开门  
5.	  {  
6.	    Serial.println("OK,Open door!\n");  
7.	    cardvalidity = 1;  
8.	    doorOpen = 1;//解决因为开门状态延迟导致的物联网上传延迟  
9.	    aliyunUpload();//解决延迟在这里忽略间隔强制上传  
10.	    doorOpen = 0;   
11.	    doorControl(1); //开门  
12.	    delay(3000);//开门状态保持3秒  
13.	    doorControl(0);//关门  
14.	    //读取的卡ID字符串和密码清零  
15.	    readID = "";  
16.	    inputPwd = "";  
17.	  }  
18.	  else  
19.	  {  
20.	    cardvalidity = 0;  
21.	    //Serial.println("Card Not Match!\n");  
22.	  }  
23.	}  
1.	void readCard()  
2.	{  
3.	  // 寻找新卡  
4.	  if ( ! mfrc522.PICC_IsNewCardPresent()) {  
5.	    //Serial.println("没有找到卡");  
6.	    return;  
7.	  }  
8.	  
9.	  // 选择一张卡  
10.	  if ( ! mfrc522.PICC_ReadCardSerial()) {  
11.	    Serial.println("没有卡可选");  
12.	    return;  
13.	  }  
14.	  
15.	  
16.	  // 显示卡片的详细信息  
17.	  Serial.print(F("卡片 UID:"));  
18.	  dump_byte_array(mfrc522.uid.uidByte, mfrc522.uid.size);//将卡的UID转换成字符串  
19.	  Serial.println();  
20.	  Serial.print(F("卡片类型: "));  
21.	  MFRC522::PICC_Type piccType = mfrc522.PICC_GetType(mfrc522.uid.sak);  
22.	  Serial.println(mfrc522.PICC_GetTypeName(piccType));  
23.	  
24.	  // 检查兼容性  
25.	  if (    piccType != MFRC522::PICC_TYPE_MIFARE_MINI  
26.	          &&  piccType != MFRC522::PICC_TYPE_MIFARE_1K  
27.	          &&  piccType != MFRC522::PICC_TYPE_MIFARE_4K) {  
28.	    Serial.println(F("仅仅适合Mifare Classic卡的读写"));  
29.	    return;  
30.	  }  
31.	  
32.	  MFRC522::StatusCode status;  
33.	  if (status != MFRC522::STATUS_OK) {  
34.	    Serial.print(F("身份验证失败?或者是卡链接失败"));  
35.	    Serial.println(mfrc522.GetStatusCodeName(status));  
36.	    return;  
37.	  }  
38.	  //停止 PICC  
39.	  mfrc522.PICC_HaltA();  
40.	  //停止加密PCD  
41.	  mfrc522.PCD_StopCrypto1();  
42.	  return;  
43.	}  

将卡的UID转换成Arduino封装的String类型储存

1.	void dump_byte_array(byte *buffer, byte bufferSize)  
2.	{  
3.	  readID = "";  
4.	  for (byte i = 0; i < bufferSize; i++)  
5.	  {  
6.	    //将uid转成字符串  
7.	    readID += buffer[i];  
8.	    Serial.print(buffer[i] < 0x10 ? " 0" : " ");  
9.	    Serial.print(buffer[i], HEX);  
10.	  }  
11.	  Serial.println('\n');  
12.	  Serial.print(readID);  
13.	}  

在这段舵机控制代码中读取了舵机当前的角度,避免错误的发生。

1.	void doorControl(int op)//0关门,1开门  
2.	{  
3.	  int pos = 0;  
4.	  int cur = door.read();  
5.	  if (op) //开门  
6.	  {  
7.	    if (cur >= 90)  
8.	    {  
9.	      Serial.println("Door has already been opened! ");  
10.	      return ;  
11.	    }  
12.	    for (pos = cur; pos <= 90; pos++)  
13.	    {  
14.	      door.write(pos);  
15.	      delay(10);  
16.	    }  
17.	  }  
18.	  else  
19.	  {  
20.	    if (cur <= 0)  
21.	    {  
22.	      Serial.println("Door has already been closed! ");  
23.	      return ;  
24.	    }  
25.	    for (pos = cur; pos >= 0; pos--)  
26.	    {  
27.	      door.write(pos);  
28.	      delay(10);  
29.	    }  
30.	  }  
31.	}  

另外请注意为了保证第一次开门舵机能顺利转动,需要在setup()函数中先将舵机复位

door.write(0);//舵机启动时复位

物联网模块

物联网模块是由一个基于MQTT协议的阿里云库aliyun_mqtt.h和aliyun_mqtt.cpp的基础上改写的,在主ino文件中物联网上传数据的入口是aliyunUpload()函数。为了让数据上报更加及时,将上传间隔改成1秒,并在开门时通过doorOpen变量忽略上传时间间隔上报一次数据。在该函数中将要上报的数值赋值给了专门定义的上传变量,它们的命名为upload_xxx。

1.	void aliyunUpload()//在loop中上传数据  
2.	{  
3.	  if (millis() - lastMs >= 1000 || doorOpen) //每1秒读取一次本地数据或强制上传  
4.	  {  
5.	    lastMs = millis();  
6.	    mqtt_check_connect();//连接阿里云IOT平台  
7.	    //将进行格式转换并串口显示  
8.	    upload_mode = MODE;  
9.	    upload_cardvalidity = cardvalidity;  
10.	    upload_detect = Detected;  
11.	    mqtt_interval_post();//ESP8266向阿里云IOT平台上报本地数据  
12.	  }  
13.	  mqttClient.loop();  
14.	}  

这是处理远程控制下传数据的代码,实现的是远程操控的功能。这段代码是在此前实验原有代码基础上增加修改而成的。有的远程操控为了保证不延迟不是通过控制变量来实现的,而是直接改变,如LED。阿里云lot发送的是JSON格式的报文,需要使用ArduinoJson库来进行解析。

在阿里云平台发送数据时选择的数据类型有限,例如说为了下传设定的密码,阿里云平台的字符串显示的是Text类型,实际上可以在Arduino端使用String来解析。额外增加的密码开锁功能在这里也引起了一点麻烦,例如需要处理输入密码为空的情况和未设定密码就使用密码开锁的情况,然而这并不是这个项目的重点,读者可以参考代码自行理解。

在开发过程中曾遇到一个严重的问题,在使用lot Studio开发出来的Web操控端的UI按键操控设备功能时,会发生数据覆盖的问题——然而在使用设备调试功能手动输入值发送时并不会出现这个问题。(一点击开关灯的按钮,模式就自动切换成自动模式了)经调试发现,使用Web操控端控制单一按钮,只发送该页面组件绑定的事件的JSON数据包,其它值均为缺省项;而在设备调试功能中是发送所有可读写的值。

查阅ArduinoJSON官方文档后发现是由于解析未在JSON报文中的数据时,将会用默认值来代替,这个默认值(数值的默认值是0)会与状态的含义和接下来要进行的操作发生冲突。

继续查阅,发现可通过更改默认值的方法来解决,将数值数据的默认值设置为-1,就不会发生冲突了。

 

1.	//处理ESP8266向阿里云IOT平台订阅的其他客户端的主题  
2.	void mqtt_callback(char *topic, byte *payload, unsigned int length)  
3.	{  
4.	  //如果云端虚拟设备发布了本地设备已订阅的主题的消息,串口显示有消息进来,并显示消息的主题(topic)和消息体(payload)  
5.	  //串口显示Message arrived [topic],其中topic是消息的主题,具体内容是阿里云平台上任意可以订阅的主题,我们关心的是ALINK_TOPIC_PROP_SET  
6.	  Serial.print("Message arrived [");  
7.	  Serial.print(topic);  
8.	  Serial.println("] ");  
9.	  //payload字符串长度不确定,将其放入StaticJsonBuffer后有剩余的部分用\0字符填充,并标识字符串结束  
10.	  payload[length] = '\0';  
11.	  Serial.println((char *)payload);//串口显示消息体(payload)的内容  
12.	  
13.	  //在topic中检索ALINK_TOPIC_PROP_SET第一次出现的位置,如果topic中第一次出现了ALINK_TOPIC_PROP_SET,  
14.	  //返回ALINK_TOPIC_PROP_SET的地址;如果没有检索到ALINK_TOPIC_PROP_SET,返回NULL  
15.	  if (strstr(topic, ALINK_TOPIC_PROP_SET)) {  
16.	    //Json对象 对象树的内存工具 静态buffer  
17.	    //100是静态buffer的大小。如果这个Json对象更加复杂,那么就要根据需要去增加这个数值  
18.	    StaticJsonBuffer<200> jsonBuffer;  
19.	  
20.	    //创建最外层的json对象:root对象,顶节点  
21.	    //解析JSON对象字符串,将JSON格式的payload消息拆分开  
22.	    JsonObject &root = jsonBuffer.parseObject(payload);  
23.	    // https://arduinojson.org/v5/assistant/  json数据解析网站  
24.	    int params_LightSwitch = root["params"]["LightSwitch"] | -1; //完成解析后,可以直接读取params中的各个变量参数值  
25.	    int params_ModeSwitch = root["params"]["upload_mode"] | -1;//默认赋值问题非常重要  
26.	    String params_legalPwd = root["params"]["legal_pwd"] ;  
27.	    String params_inputPwd = root["params"]["input_pwd"] ;  
28.	    if (params_legalPwd.length() > 0)  
29.	      legalPwd = params_legalPwd;  
30.	    else if (!params_inputPwd.length())  
31.	      Serial.println("设置密码为空请重新输入");  
32.	    if (params_inputPwd.length() > 0)  
33.	    {  
34.	      inputPwd = params_inputPwd;  
35.	      Serial.printf("%s %s\n", inputPwd, legalPwd);  
36.	    }  
37.	    else if (!params_legalPwd.length())  
38.	      Serial.println("输入密码为空请重新输入");  
39.	    //如果读到了所关心的变量,可以执行进一步的操作,这里是用LightSwitch变量开灯或关灯  
40.	    if (params_ModeSwitch == 1) //先切换模式再控制灯,防止出现同时更改灯和模式状态出现自动模式不能控制灯的情况  
41.	    {  
42.	      Serial.println("Switch to Manual Mode");  
43.	      MODE = 1;  
44.	    }  
45.	    else if (params_ModeSwitch == 0)  
46.	    {  
47.	      Serial.println("Switch to Auto Mode");  
48.	      MODE = 0;  
49.	    }  
50.	    if (!MODE)  
51.	    {  
52.	      MODE = 1;  
53.	      Serial.println("Switch to Manual Mode due to control remotely");  
54.	    }  
55.	    if (params_LightSwitch == 0)  
56.	    {  
57.	      Serial.println("led off");  
58.	      digitalWrite(LED, 1);  
59.	      remoteLED = 1;  
60.	    }  
61.	    else if (params_LightSwitch == 1)  
62.	    {  
63.	      Serial.println("led on");  
64.	      digitalWrite(LED, 0);  
65.	      remoteLED = 1;  
66.	    }  
67.	  
68.	    if (!root.success())//如果解析没成功,串口输出解析失败(parseObject() failed)  
69.	    { Serial.println("parseObject() failed");  
70.	      return;  
71.	    }  
72.	  }  
73.	}  

 

远程控制页截图(设备未在线) 

点击设置密码进入密码设置页

阿里云功能定义

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/545830.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Unity UI -- (4)用图像创建菜单背景

添加一个基础的设置菜单背景 设置菜单的元素会安放在一个简单的矩形区域上。我们用一个Image对象来创建这个矩形。 1. 首先&#xff0c;我们暂时停用Title Text和Settings Button游戏物体。这样会让我们的Canvas看起来更清爽。 2. 在Hierarchy中&#xff0c;点击右键&#xff0…

探索云原生世界:当前最受欢迎的技术和趋势

文章目录 探索云原生世界&#xff1a;当前最受欢迎的技术和趋势引言&#xff1a;一、云原生概述&#xff1a;1. 什么是云原生&#xff1f;2. 为什么云原生重要&#xff1f;3. 云原生的核心原则和特征。4. 云原生的优势和挑战。 二、核心技术与工具&#xff1a;1. Kubernetes&am…

C语言qsort函数、活字印刷、cmd窗口

一、qsort函数 qsort函数就是快排&#xff0c;可以不用写那么一长串的代码了qvq&#xff0c;要用到stdlib.h库文件 那么具体用法就是 oid qsort(void* base,size_t num,size_t width,int(__cdecl*compare)(const void*,const void*)); 当然我们还要用一个比较函数来确定快排…

手机APP性能测试工具PerfDog性能狗安装教程及简单使用

一、前言 PerfDog是一个由腾讯研发的主流性能测试软件。可以提高软件和游戏的运行效率&#xff0c;支持iOS/安卓在移动平台上的性能测试和分析&#xff0c;快速定位和分析性能问题等。无需安装&#xff0c;即插即用&#xff0c;减少繁琐的测试障碍&#xff0c;安卓设备不需要RO…

PCB基础~PCB介质,Vias

PCB介质 • 一般的介质材料 – FR-4&#xff08;玻璃纤维和环氧基树脂交织而成&#xff09; • 最常和最广泛使用&#xff0c;相对成本较低 • 介电常数&#xff1a;最大4.7&#xff0c; 4.35500Mhz,4.341Ghz • 可承受的最高信号频率是2Ghz(超过这个值&#xff0c;损耗和串扰…

IDEA中怎么把jar包导入项目中

大作业让生成一个pdf&#xff0c;查找资料发现可以通过pdfbo相关函数调用&#xff0c;但本地缺少这个文件&#xff0c;以这个文件为例子。 一、下载 下载去Apache上下载&#xff0c;Apache PDFBox | Download&#xff0c;&#xff0c;结合自己的java版本啥的下载就行。 我是…

java中使用java8的stream报错java.lang.IllegalStateException: Duplicate key

一、java.lang.IllegalStateException: Duplicate key报错的原因 map的key重复导致的报错Duplicate key 二、java.lang.IllegalStateException: Duplicate key报错的解决方式 list.stream().collect()就是把一个List的查询数据集合转为一个Map&#xff0c;java8的stream方式…

UE5 C++类如何打印日志?

UE5 插件开发指南 前言0. 什么是日志?1.在哪里可以查看日志呢?2. 日志有哪些等级?3. 如何打印到屏幕上?4. 如何更专业的记录日志?4.0 UE_LOG宏语法4.1 自定义日志类别4.2 插件中的日志类别定义前言 在回答这个问题之前,先要给萌新科普一下:什么是日志?以及,在哪里查看日…

自己动手写一个加载器

前言 当在 linux 命令行中 ./ 运行一个程序时&#xff0c;实际上操作系统会调用加载器将这个程序加载到内存中去执行。为了探究加载器的行为&#xff0c;今天我们就自己动手写一个简单的加载器。 工作原理 加载器的工作原理&#xff1a; 从磁盘读取 bin 文件到内存&#xf…

【Python html常用标签】零基础也能轻松掌握的学习路线与参考资料

学习路线 要深入了解Python html常用标签&#xff0c;需要遵循以下学习路线&#xff1a; 1.1 HTML基础知识&#xff1a;了解HTML语言的起源&#xff0c;HTML文档结构和基本标签。学习HTML标签包含但不限于文本标签&#xff0c;图像标签&#xff0c;链接标签&#xff0c;表格标…

canvas的HTML和JavaScript

文章目录 一、canvas元素二、前期准备1. 坐标系2. canvas属性① 获取canvas元素② 把canvas实例化为2D③ 设置路径颜色④ 设置路径宽度⑤ 设置路径末端形状⑥ 设置路径相连时的相连部分形状⑦ 透明度⑦ 虚线 三、绘制图行1. 绘制线段2. 绘制三角形① 空心三角形② 实心三角形 3…

大学四年,我建议你这么学网络安全

在所有关注我的朋友中&#xff0c;大致分为两类&#xff0c;一类是社会人士&#xff0c;有的是安全老手&#xff0c;有的是其它工作但对安全感兴趣的朋友&#xff0c;另一类应该就是大学生了。 尤其随着国家的号召和知识的普及&#xff0c;越来越多的人开始对网络安全感兴趣&a…

ffmpeg日记1011-过滤器-语法高阶,逻辑,函数使用

Author: wencoo Blog&#xff1a;https://wencoo.blog.csdn.net/ Date: 19/05/2023 Details:文章目录 摘要什么是时间线编辑哪些filter支持时间线编辑时间线编辑中&#xff0c;可以使用哪些预设函数常用预设函数功能即使用方法if(x, y)if(x, y, z)gt(x, y)gte(x, y)lt(x, y)lte…

AHB-to-APB Bridge——04apb_tran、apb_if、apb_drv、mem、apb_mon、apb_agt

apb_if放入所有apb需要的信号&#xff0c;以及cb ifndef APB_IF_SV define APB_IF_SVinterface apb_if;logic pclk;logic prst;logic penable;logic …

C++的stack和queue

stack和queue 1.stackstack的模拟实现 2.queuequeue的模拟实现 3.容器适配器3.1. 什么是容器适配器3.2. STL标准库中stack和queue的底层结构3.3. deque的简单介绍3.3.1. deque原理介绍3.3.2. deque的缺陷3.3.3. 为什么选择deque作为stack和queue的底层默认容器 1.stack stack的…

深度学习-第T7周——咖啡豆识别

深度学习-第T7周——咖啡豆识别 深度学习-第T7周——咖啡豆识别一、前言二、我的环境三、前期工作1、导入数据集2、查看图片数目 四、数据预处理1、 加载数据1、设置图片格式2、划分训练集3、划分验证集4、查看标签 2、数据可视化3、检查数据4、配置数据集 五、搭建CNN网络六、…

Vue3+TS知识点补充

一、关于Ref 1.shallowRef() shallowRef 是 Vue 3 中新引入的响应式数据类型之一&#xff0c;它与 ref 类型非常相似&#xff0c;但是有一些不同点。 不同的是&#xff0c;shallowRef 只会对其包装的对象进行浅层次的响应式处理&#xff0c;即如果这个对象的子属性发生改变&…

软件测试——黑盒测试

1.测试概述 1.1综述 本测试报告为计算机程序能力在线测评系统的黑盒测试&#xff0c;黑盒测试可以在不知道程序内部结构和代码的情况下进行&#xff0c;用来测试软件功能是否符合用户需求&#xff0c;是否达到用户预期目标&#xff0c;是否拥有较好的人机交互体验。 图1.1 黑…

media设备节点初始化与Video4Linux初始化

media设备节点初始化与Video4Linux初始化 文章目录 media设备节点初始化与Video4Linux初始化media设备节点初始化Video4Linux初始化 media设备节点初始化 media_devnode_init函数是一个内核初始化函数&#xff0c;用于在Linux内核启动期间进行设备节点初始化。 函数的主要作用…

复习:遥感图像解译复习整理

惭愧&#xff0c;这个课程从始自终就没有认真学过&#xff0c;一部分是因为自己的原因&#xff0c;另一部分也是因为自己的原因。因此&#xff0c;对于整理的资料有不足之处请指正。 另外&#xff0c;资料自word复制&#xff0c;没有时间整理博客的格式。 -- 2023年05月19日记…