STC11F04E——电子工艺实习

时间:2024-03-11 16:41:19

这几天做了一下电子工艺实习,焊接了PCB然后烧写了相应的程序,现在将实验过程记录下来。

声明1:我是一个单片机小白,下面有任何说的不对的地方,恳请各位指正,谢谢。
声明2:我将控制位选的4个三极管更换了,程序由原来的位选高电平选中,换成了位选低电平选中,在此声明。
声明3:这款PCB还实现了超声波测距、数码管滚动显示学号等功能,请见另一篇博客,代码详见GitHub

我已将该项目上传到GitHub,项目地址为: STC11F04E项目。 代码开源,欢迎测试

一、实验平台介绍

  本次实验使用的PCB是老师自己画的,老师给了一张并不清晰的原理图截图,如下图所示。可以看到,这块PCB能实现三个功能:使用DS1302显示时间、使用DS18B20显示温度以及使用HC-SR04进行超声波测距(原理图上没有画,实际上它是与DS1302复用了IO和2SLCK引脚),数值显示都是用数码管。实验用到的主控芯片是:STC11F04E
在这里插入图片描述

图1 原理图

  焊接完成后大概长这样:
在这里插入图片描述

图2 正面

在这里插入图片描述

图3 背面

二、功能实现

2.1 数码管驱动

2.1.1 原理

  点亮单个数码管只需要给数码管的公共端接高电平(低电平),然后再给需要点亮的那一段接低电平(高电平)就可以了,其中“给需要点亮的那一段接低电平(高电平)”的过程称为“段选”。点亮多个数码管时,不仅需要选择点亮哪一段,还需要告诉单片机你选中的是哪个数码管,称为“位选”。数码管静态显示就是通过“位选”选中,然后“段选”点亮某些数码管。动态显示就是位选和段选切换的快一些,快到我们的眼睛察觉不到动态变化,就可以显示任意数字组合了。

2.1.2 共阴还是共阳

  点亮每个数码管之前,都需要知道你的数码管是共阴的还是共阳的,这样才能点亮。测试的方法就是给公共端一个高电位,其他端口给个低点位,如果有一段被点亮了,说明这个数码管是共阳的,否则就是共阴的数码管。
在这里插入图片描述

图4 数码管测试

  上面是利用Arduino的3.3V和GND接口,测试数码管的过程,可以看到本次实验使用的5611BH数码管是共阳的数码管。

2.1.3 编写驱动程序

  先看一下这部分的原理图
在这里插入图片描述

图5 数码管部分原理图

  可以看到原理图中控制位选的是P1.0~P1.3这四个IO口,控制数码管段选的是74HC164这个芯片。

  • (1)位选部分
      刚才已经测量过,实验使用的数码管是一个共阳的数码管,所以位选选中的话就需要给每一个公共端高电平。公共端连结了一个三极管,三极管的基极连结P1口,射级连结了VCC,集电极连结数码管的公共端。因此,想要选中某一个数码管,只需使位选P1口输出高电平即可。
  • (2)段选部分
      段选控制部分用到了一个74HC164芯片,这个芯片的特点就是“串行输入、并行输出”,相当于一个8bit的移位寄存器。AB是数据输入端,它会在时钟的上升沿输入数据;CLK是时钟信号输入;MR是复位端,将它接高电平使能;Q0~Q7是数据并行输出端,会将8Bit的数据并行输出。使用这个74HC164芯片,可以减少单片机IO口的占用,仅使用两个IO口就可以控制一个数码管了。
  • (3)程序编写
      程序就不详细解释了,我在程序里写了很多注释,现在直接贴上主要部分的代码。
/**********************************************************
*  函数名称:74HC164发送Byte函数
*  日期:2019-9-29
*  姓名:ZhangHJ
*  说明:74HC164移位寄存器输入一字节数据发给寄存器
***********************************************************/
void SendByte_74HC164(uchar byte)
{
	uchar num,c;
	num=tab[byte];
	for(c=0; c<8; c++)
	{
		DAT=num&0x01;			// P3^0 --> 0000 000x
		CLK=0;					// 制造一个上升沿
		CLK=1;
		num>>=1;				// 将数据发送到寄存器
	}
}

  上面贴出来的是74HC164发送数据的函数,也就是数码管段选的函数。位选控制的话,直接将P1口全部选中就可以了。
  这部分做完后,现在板子可以实现4位数码管同时点亮的功能了,如下图所示。拍照可能数码管不太清楚,但实际看着效果可以。
在这里插入图片描述

图6 数码管静态显示效果

点击查看静态显示的展示效果视频

2.1.4 数码管动态显示

  上面做完之后,就已经实现了数码管的静态显示,也就是4个数码管只能同时显示同一个数,要实现动态显示,就需要对数码管的位选进行详细的控制。
(1)动态原理
  一般来说,动态显示的流程是:

循环进行:

  1. 关闭位选(位选都不选中)
  2. 开启段选(74HC164向寄存器传送数据)
  3. 开启位选(选中要显示的数码管)
  4. 关闭段选(清空段选数据,防止残影)
  5. 关闭位选(防止残影)

  下面就根据这个顺序写一些数码管的动态显示程序。
(2)程序编写
  同样只贴出重要的代码,工程文件我上传到了GitHub,会在文末给出。

/**********************************************************
*  函数名称:按字节控制数码管动态显示函数
*  日期:2019-9-30
*  姓名:ZhangHJ
*  说明:实现4个数码管显示 1234 的效果
* 		数码管引脚低电平有效,首先关闭位选信号,然后给寄存器发送数据(开启段选),
*		开启位选控制并延时一段时间以显示数字,最后关闭位选信号清屏.
***********************************************************/
void test_light_byte()
{
		P1 |= 0x0f;							// 关掉位选
		SendByte_74HC164(1);				// 开启段选
		P1 &= 0xf7;							// 开启位选
		delay_ms(DELAY_TIME);				// 延时显示
		P1 |= 0x0f;							// 关掉位选
		
		SendByte_74HC164(2);
		P1 &= 0xfB;
		delay_ms(DELAY_TIME);
		P1 |= 0x0f;
		
		SendByte_74HC164(3);
		P1 &= 0xfD;
		delay_ms(DELAY_TIME);
		P1 |= 0x0f;

		SendByte_74HC164(4);
		P1 &= 0xfe;
		delay_ms(DELAY_TIME);
}

  上面的方法是同时控制1Byte的P1口的高低电平,实现位选控制,同样可以直接使用位控制,来控制数P1的某一个端口,下面给出代码。

/**********************************************************
*  函数名称:按位控制数码管动态显示函数
*  日期:2019-9-30
*  姓名:ZhangHJ
*  说明:实现4个数码管显示 4321 的效果
* 		数码管引脚高电平有效,首先设置各位段选信号,然后给寄存器发送数据
*		延时一段时间实现数字显示,最后清除段选信号清屏.
***********************************************************/
void test_light_bit()
{
		DPY0 = ON;
		DPY1 = OFF;
		DPY2 = OFF;
		DPY3 = OFF;
		SendByte_74HC164(1);
		delay_ms(DELAY_TIME);
		SendByte_74HC164(10);
	
		DPY0 = OFF;
		DPY1 = ON;
		DPY2 = OFF;
		DPY3 = OFF;
		SendByte_74HC164(2);
		delay_ms(DELAY_TIME);
		SendByte_74HC164(10);
	
		DPY0 = OFF;
		DPY1 = OFF;
		DPY2 = ON;
		DPY3 = OFF;
		SendByte_74HC164(3);
		delay_ms(DELAY_TIME);
		SendByte_74HC164(10);
		
		DPY0 = OFF;
		DPY1 = OFF;
		DPY2 = OFF;
		DPY3 = ON;
		SendByte_74HC164(4);
		delay_ms(DELAY_TIME);
		SendByte_74HC164(10);
}

  差不多的流程,同样能实现数码管的动态显示控制。动态显示做完之后,这四个数码管就能显示不同的数字了,效果如下图所示。
在这里插入图片描述

图7 数码管动态显示效果

点击查看数码管动态显示的效果视频

  可以看到,在按键按下后,显示的4321是有残影的。这是因为按位操作显示4321的函数,没有严格按照动态显示的顺序进行编写,没有关闭位选。就先这样吧,不再改了。
  数码管能做到这里基本就算完事了,接下来进行DS18B20温度传感器的控制。

2.2 DS18B20 温度传感器驱动

2.2.1 原理

  DS18B20是一个常见的温度传感器,特点就是“单总线数据传输”。因为它只有一个数据引脚,要实现单片机之间的数据读取、写入、初始化等操作,时序就非常重要了。非常非常非常重要(重要的事情说三遍)。其他内容看一下资料就好了,资料在我的GitHub里面也有。

2.2.2 程序编写

  我们想实现的功能是将DS18B20的温度数值,显示在这4个数码管上。数码管已经玩过了,下面就列举一下DS18B20的驱动程序。

/**********************************************************
*  函数名称:发送复位和初始化命令函数 dsinit
*  修改日期:2019-9-9
*  修改人:ZhangHJ
*  说明:1. 对于单片机: 单片机首先发出480-960us的低电平脉冲,
*			释放总线为高电平(上拉电阻拉高),在随后的480us进行检测,
*			如果出现低电平,说明器件应答正常.B
*		2. 对于DS18B20: 上电后就检测是否有480/960us的低电平脉冲,
*			如果有低电平,在总线释放之后,等待15-60us,
*			将电平拉低60-240us,告诉主机已经准备好.
***********************************************************/
uchar dsreset(void)					// send reset and initialization command
{
  uint i;
  DS = 0;							// 先将端口拉低
  i=120;							// 维持低电平状态480us~960us
  while(i>0)i--;
  DS = 1;							// 然后释放总线(将总线拉高),若DS18B20做出反应,将会将在15us~60us后将总线拉低
	
	i = 0;
	while(DS)						// 在DS高电平时等待
	{
		i++;
		if(i > 50000)				// 等待时间大于60us,说明响应失败
		{
			return 0;
		}
	}
	return 1;
}

  上面这个程序是DS18B20的初始化函数,也就是说,DS18B20在于单片机进行通信之前,需要跟单片机告诉一声,单片机于DS18B20之间建立通信连接之后,才能进行整行的通信。这也是单总线通信的特点。
  大概的通信过程是:

  1. 对于单片机: 单片机首先发出480-960us的低电平脉冲,释放总线为高电平(上拉电阻拉高),在随后的480us进行检测,如果出现低电平,说明器件应答正常。
  2. 对于DS18B20: 上电后就检测是否有480/960us的低电平脉冲,如果有低电平,在总线释放之后,等待15-60us,将电平拉低60-240us,告诉主机已经准备好。

  单片机与DS18B20之间建立通信连接(DS18B20初始化)完成后,单片机想读取DS18B20的寄存器中的数据,还需要写一个读取数据的函数,下面贴出代码。

/**********************************************************
*  函数名称:读1bit数据函数
*  修改日期:2019-9-9
*  修改人:ZhangHJ
*  说明:1.首先单片机端口拉低 >1us,
*		2.然后释放总线,拉高总线,
*		3.等待>15微秒,是为了让 DS18B20 数据稳定,
*		4.读取DS数据,
*		5.接下来进行延时等待采样周期完成。
*	详见DS18B20资料2.8.3.1读/写时间片
***********************************************************/
bit tmpreadbit(void)				// read a bit data
{
	uint i;
	bit dat;						// 定义位数据 (dat = 0 or 1)
	DS = 0;							// 先将端口拉低
	_nop_();						// 延时 2us ,要求至少保持1us
	DS = 1;							// 再将端口拉高
	i=8;while(i>0)i--;				// 等待DS数据稳定,要求的至少延时15us以上
	dat = DS;						// 数据传输
	i=15;while(i>0)i--;				// 等待数据采样周期完成,要求不低于60us
	return (dat);
}

  上面这个函数,是单片机读取DS18B20一位数据的函数。这个函数的时序要求很严格,这些操作的时间一定要按要求,否则程序将不能正常运行。函数具体的过程也不说了,注释里都有。现在这个函数是读取1bit数据的函数,要想实现1Byte的读取,只需将这个函数循环8次,加上数据移位操作就可以了,代码就不贴了。
  上面已经算是单片机可以读取到DS18B20的数据了,但是DS18B20它是怎样得到温度数值的呢?这涉及到了对DS18B20 的寄存器写指令的操作,举个例子,单片机往DS18B20里面写入一字节的指令0x44,18B20就会进行进行一次温度转换。这些操作指令在DS18B20的手册里面都有,我们也不用记住,到时候直接手册就行。所以现在需要写一个写操作的函数,用来给DS18B20写指令,下面贴出代码。

/**********************************************************
*  函数名称:写入1Byte数据函数
*  修改日期:2019-9-11
*  修改人:ZhangHJ
*  说明:1.将对待写入数据dat进行位操作,将dat末位数值赋值给位数据testb
*		2.通过判断testb得到写 0 还是写 1
*		3.若是写 0 操作,将 DS 拉低,进行>60us的延时,再将DS拉高,进行>1us的延时
*		4.若是写 1 操作,将 DS 拉低,进行15~60us的延时,再将DS拉高,进行>60us的延时
*		5.循环执行2、3、4操作8次,写入1字节数据
*	详见18B20资料“2.8.3.1读/写时间片”章节
***********************************************************/
void tmpwritebyte(uchar dat)   		//write a byte to ds18b20
{
  uint i;
  uchar j;
  bit testb;
  for(j=1;j<=8;j++)
  {
    testb = dat&0x01;
    dat = dat>>1;
    if(testb)     					// write 1
    {
      DS=0;
      i=8;while(i>0)i--;;			// 延时要求15~60us内
      DS=1;
      i=15;while(i>0)i--;			// 要求不低于60us
    }
    else
    {
      DS = 0;       				// write 0
      i=15;while(i>0)i--;
      DS = 1;
      i++;i++;
    }
  }
}

  写操作同样对时序也是非常非常严格,按手册要求才行。具体的过程同样也没啥可说的,注释里也有。这样就可以对DS18B20写入操作命令,指示DS18B20干活了。

/**********************************************************
*  函数名称:18B20温度转换完整过程函数
*  修改日期:2019-9-11
*  修改人:ZhangHJ
*  说明:1.首先进行18B20初始化
*		2.进行适当延时
*		3.发送跳过光刻ROM指令
*		4.发送RAM指令,进行温度转换
*		5.读取两个8位数据,放到16位寄存器 temp 中
*		6.将读取到的二进制数据(默认为正数),转换为十进制数据
*		7.返回温度数据
*	详见18B20资料“2.8.3.1存储器操作命令”章节
***********************************************************/
uint tmp()								// get the temperature
{
  float tt;
  uchar high,low;
	//P1 |= 0x0f;
  while(dsreset() == 0)
	{
		SendByte_74HC164(0);
		P1 &= 0xfB;
	}
  delay(1);
	//tmpchange();
  tmpwritebyte(0xCC);					// 跳过 ROM 操作
  tmpwritebyte(0xBE);					// 读暂存寄存器
  low=tmpread();
  high=tmpread();
  temp=high;
  temp<<=8;								// two byte compose a int variable
  temp=temp|low;
  tt=temp*0.0625;
  temp=tt*10+0.5;
  return temp;
}

  上面的函数就是DS18B20温度转换的完整过程,因为涉及到了一些寄存器的操作,温度数值转换的操作,我也没深入研究,也不说啥了。想要知道原理的话可以看一看DS18B20的手册。
  现在已经能够实现读取DS18B20温度数值的功能了,只要将温度转换之后的数值,显示到数码管上就完事了。数码管那一部分就不贴代码了,跟动态显示差不太多。效果如下图所示。
在这里插入图片描述

图8 温度显示效果

点击查看DS18B20温度传感器测试视频

2.3 DS1302时钟模块

2.3.1 原理

  DS1302是一个常见的时钟芯片,它可以可提供秒、分、时、日、星期、月和年的时间记录,同时还有每月多少天的自动调整,还具有闰年补偿功能。嗯,功能多到我们都用不着哈?。这东西拿来做一个万年历还行,但是我的PCB上面只有4个数码管,一次也显示不全,还不如只用“时分”功能来得方便。
  DS1302的封装就长这样:
在这里插入图片描述

图9 DS1302时钟芯片

Vcc2是它的主电源,Vcc1是它的备用电源
X1、X2是晶振的输入端
SCLK是时钟的输入端
CE是片选使能

  在芯片手册里有要求,外接的晶振是标准的32.768KHz,按要求外接一个就行了。SCLK接单片机的引脚,因为这个芯片在读写操作时需要进行时钟的控制(在时钟上升沿写入数据;在时钟下降沿读数据),所以接到单片机上给它造上升沿和下降沿。CE是片选使能,在时序图里面可以看到,要进行读写操作时,CE必须要是高电平。低电平的CE会禁止读写操作,终止数据的传输。
在这里插入图片描述

图10 DS1302时序图

2.3.2 程序编写

  DS1302这个芯片操作还是比较方便的,这个芯片因为是专门用来当时钟的芯片,所以对他的访问也无非就只有“写入初始时间”和“读取时间”。
  对于写入初始时间来说,也就是往DS1302寄存器里写入数据而已;对于读取时间来说,需要你提供一个地址,用来告诉单片机你想访问DS1302的哪个寄存器,然后单片机就会去DS1302这个寄存器里面取值,经过数值转换后,就成了我们想要的时间数值。
  DS1302的寄存器也有点意思,因为它是专门的时钟芯片,所以寄存器也是很好理解。
在这里插入图片描述

图11 DS1302寄存器

  首先来进行写命令函数的编写,下面贴出代码。

/**********************************************************
*  函数名称:DS1302写操作函数
*  日期:2019-10-4
*  姓名:ZhangHJ
*  说明:写操作包含两个参数,add表示要写入的地址;wdata表示要写入的数据
*		1. 读写操作需要先将RST拉高才能进行
*		2. 先写入控制字节,在时钟上升沿串行写入数据
*		3. 再写入数据字节,同样在时钟上升沿写入数据
*		4. 最后拉低RST禁止数据传输
***********************************************************/
void DS1302Write(uchar add,uchar wdata)
{
	uchar a;
	//wdata = hex(wdata);				// 转换为BCD码
	RST=0;								// 拉低RST引脚,终止数据传输
	SCLK=0;								// 拉低SCLK引脚,清零时钟线
	RST=1;								// 拉高RST引脚,所有数据传输都要拉高RST脚,启动控制逻辑
	//先写入控制字节
	for(a=0; a<8; a++)
	{
			IO= add & 0x01;				// IO引脚准备好要写入的1位数据
			SCLK=1;						// SCLK上升沿,1位数据从IO脚写入,低位先写入
			add>>=1;					// 数据右移1位
			SCLK=0;						// 拉低SCLK,为下次写入准备,循环8次写入1字节
	}
	//再写入数据字节
	for(a=0; a<8; a++)
	{
			IO= wdata & 0x01;
			SCLK=1;
			wdata>>=1;
			SCLK=0;
	}
	RST=0;								// 数据传输完拉低RST
}  

  写入数据也是串行写入的,可以依照时序图进行该函数的编写。另外读命令函数也贴出代码。

/**********************************************************
*  函数名称:DS1302读操作函数
*  日期:2019-10-4
*  姓名:ZhangHJ
*  说明:读操作只需一个参数,即需要读取的寄存器地址add
*		1. 首先需要将需要读取的地址(1Byte)写入寄存器,在上升沿进行写入操作
*		2. 之后读取该地址中的数据,在下降沿读取数据
*		3. 最后返回读取到的1Byte数据
***********************************************************/ 
uchar DS1302Read(uchar add)
{
	uchar a, rdata=0;
	RST = 0;									// 拉低RST引脚,终止数据传输
	SCLK = 0;									// 拉低SCLK引脚,清零时钟线
	RST = 1;									// 拉高RST引脚,启动控制逻辑
	//发送控制字节
	for(a=0; a<8; a++)
	{
		SCLK = 0;
		IO = add & 0x01;
		SCLK = 1;								// 制造一个上升沿,写入地址
		add >>= 1;
	}
	//读1字节数据
	for(a=0; a<8; a++)
	{
		SCLK = 1;
		rdata >>= 1;
		SCLK = 0;								// 制造一个下降沿,读取数据
		if(IO)
		{										// 如果读到1
			rdata |= 0x80;						// 把最高位置为1,记录到rdata中
		}
	}
	RST=0;										// 拉低RST
	//return dec(d);        					// 读取的数据转换成十进制
	return rdata;
}

  读命令也是依照时序图就可以编写出来,方便理解。
  现在读写指令都编写完成了,基本上DS1302的功能就可以实现了。其他的数码管显示、初始时间设置等等,就不再说了,直接看GitHub。效果如下图所示。
在这里插入图片描述

图12 时间显示效果

点击查看DS1302时钟芯片测试视频

2.4 代码整合

  现在已经实现了数码管显示、DS1302获取时间、DS18B20获取温度这三个功能了,只要将代码整合一下就能烧到单片机中了。具体的整合过程也不再赘述了,只贴一下最终的主函数。

// 主函数功能:默认显示时间(初始化时间为“2019年10月4日18:55:00”),按住INT0按键时,会显示温度
void main()
{
	uchar a;
	ds1302_init();								// DS1302日期初始化
	while(1)
	{
		// 时间显示
		read_time();							// DS1302读取当前时间
		Display_Time();							// 显示当前时间
		// 按键处理
		if (KEY == 0)
		{
			delay(5);
			while(KEY == 0)						// 按键消抖
			{
				// 温度显示
				tmpchange();					// 首次温度转换
				for(a=50;a>0;a--)				// 延时,保持连续显示
				{
					Display_Tmp(tmp());			// 进行温度转换和数值显示
				}
			}
		}
	}
}

  主函数实现的功能就是,在单片机上电后,进行一次时钟芯片的初始化,设置初始时间,然后循环进行时间的显示。当INT0按键被按下时,会进行温度转换,显示实时温度。
点击查看代码整合视频

三、设想

  现在单片机能实现两个功能:温度显示、时间显示 。但是时间的显示是通过DS1302时钟芯片来实现的,这就有一些缺点。比如我可能需要在初始化时钟芯片时写入初始化的时间,这就显得不太合适;同时,由于单片机晶振和外部晶振的问题,可能导致时间不准确,过一段时间还得手动校时,这也很不方便。
  比较好的解决办法是将单片机联网,在网络获取时间后写入DS1302,每隔一段时间进行一次自动校时,这样就解决了时间不准确的问题。然而单片机自身不能联网,需要通过其他方式接入网络。ESP8266就是一款小巧的WiFi模块,通过单片机与ESP8266的通信,可以将网络时间传输到单片机中。
  ESP8266可以通过AT指令的方式,与单片机进行数据通信;同时,它自身的WiFi功能还能通过网络GET请求获取到NTP服务器的精确时间,通过串口传给单片机进行校时。
  目前我已实现使用ESP8266获取到网络时间的功能,我是用TTL连接的电脑,用串口助手给ESP8266发送AT指令,通过GET请求获取到了NTP服务器的时间,如下图9所示。
  在GET请求到的数据中,可以看到date1就是我们想要的时间,而且还是UTC +8.00的东八区时间,用起来会更方便了。
  我现在是使用串口与ESP8266通信,需要实现的单片机与ESP8266的通信,与这种方式差不多,所以实现起来还是可行的。
在这里插入图片描述

图13 串口助手

四、总结

  以前玩过Arduino、树莓派,现在看来Arduino,它的IDE是很方便,适合初学者;而树莓派因为能搭载Linux所以功能太强大,跟电脑差不多了。还是单片机好玩,因为在学习单片机的过程中,才能真正理解每一个器件是如何工作的,学习工程中需要了解很多底层的知识才能玩转。
  单片机一开始上手都不知道怎么烧程序,后来查阅资料之后才慢慢了解。另外就是DS18B20 时序很重要。。。因为我花了很长时间才把它玩起来,让人头大。

五、资料

5.1 本项目GitHub地址

https://github.com/ZHJ0125/STC11F04E

5.2 参考资料

(1)74HC164

74hc164中文资料汇总(74hc164引脚图及功能_特性参数及典型应用电路) - 全文 - 电子发烧友网
74HC164_中文资料_价格_官方数据手册_STMICROELECTRONICS - 万联芯城
51单片机74HC164串口控制数码管显示
74HC164控制数码管显示 - 恶魔的旋律 - ITeye博客
74HC164驱动程序实例(C语言版子程序或汇编版子程序)-百合电子工作室
STC Micro STC11F04E - PDF Datasheet - STC In Stock | lcsc.com
74HC164 - ztm521的专栏 - CSDN博客

(2)AT89C2051

AT89C2051-24SU_引脚图_电路图(2/10)_ATMEL - 万联芯城
AT89C2051-24PC 89C2051-24PC 价格

(3)DS18B20

单片机练习 - DS18B20温度转换与显示 - MK2 - 博客园
16.4 温度传感器 DS18B20 - 单片机教程(三) - 极客学院Wiki
关于DS18B20温度传感器的时序详解及代码分析 - Kk_01110001B的博客 - CSDN博客
51单片机的几种精确延时 - feike24的博客 - CSDN博客

(4)DS1302

51单片机操作DS1302时钟芯片 - 柚柚控 - ITeye博客
51单片机DS1302实时时钟驱动程序 - Line - CSDN博客

(5)ESP8266

ESP8266-01 固件更新过程 - qq_31310793的博客 - CSDN博客
工具 | 乐鑫
v1.1.1 · GitBook

使用ESP8266 NodeMCU从NTP服务器获取日期和时间
ESP8266 NTP时钟| 坚果与伏特杂志
认识ESP8266 | 坚果与伏特杂志
带OLED的最简单ESP8266本地时间互联网时钟:4个步骤(带图片)

51单片机利用8266获取网络时间 - weixin_42757674的博客 - CSDN博客

STC: 1T 8051 单片机创新者, ISP/IAP 技术创新者
三极管是如何导通的? - 知乎