蓝桥杯电子类单片机提升一——超声波测距

news2024/11/27 12:57:14

前言

单片机资源数据包_2023

一、超声波测距原理

二、超声波测距的应用

1.超声波的发射

2.单片机知识补充:定时器

3.超声波的接收与计时

4.距离的计算

1)定时器1为16位自动重载+1T@11.0592MHz

2)定时器1为16位自动重载+12T@11.0592MHz

3)定时器1为16位自动重载+1T@12MHz

4)定时器1为16位自动重载+12T@11.0592MHz

总结

5.代码完善

 三、完整代码展示


前言

关于蓝桥杯比赛时会提供的资料前几篇都有提到,这里就不在赘述了,只放一个下载链接:

单片机资源数据包_2023

除了基础部分的按键、LED灯,数码管扫描,还有温度传感器,AD/DA转化,EEPROM存储器,RTC之外,还有三个模块考试的时候可能会考,分别是超声波,NE555和串口。近几年的题也是越来越难,这三个模块也逐渐出现在了省赛的舞台上(当然如果进国赛了,这几个模块就都可能考了)。提升篇主要针对这三个模块进行介绍。

由于这三个模块比赛时不会提供底层代码,所以许多都需要咱们自己来完成,所以不同人写的代码,差异性可能会更大。此外这些代码会涉及到单片机运行的底层知识,关于单片机基础部分的内容,提升篇也会尽可能介绍一部分(当然如果你不会也没关系,文章会教你如何用stc生成或者查数据手册,就算不知道原理,小背一背也是能自己实现的)

一、超声波测距原理

超声波测距的原理很简单,就是超声波发送装置先发送一段超声波,然后开始计时,一直等到接收端接收到反射回来的超声波,然后停止计时。记录的这段时间T,就是超声波碰到物体在返回的时间,超声波的速度一般取340m/s,那么物体距离超声波测距装置的距离S=V*T/2.很好理解,这里就不在画图进行过多介绍了。

发射

接收 

 这两张分别是在原理图上的超声波发射和接收电路,这里也不过多介绍。

在使用超声波之前,我们需要正确配置跳线帽。 也就是将J2 2x3的排针的A1和A相连,B1与B相连,这样超声波发送端就与P10相连了,接收端就与P11相连了。换言之,超声波的数据发送引脚TX=P10;数据接收引脚RX=P11。

二、超声波测距的应用

第一章已经介绍了超声波测距的原理,无非是发送,计时,接收信号+数据处理(把时间信息转化为距离信息),接下来咱们一一实现着三个步骤

1.超声波的发射

超声波的发送,就是需要我们通过TX引脚发送8个40KHz的超声波信号(具体为什么测距需要用40KHz的超声波,可以自行百度)。

我们知道,40KHz的信号,一个周期就是25微秒,那半个周期就约13us。理论上我们只需让TX置为1,然后延时13us,然后在让TX置为0,再延时13us就完成了一个40KHz信号的发射。放在一个for循环内,即可完成发送8个40KHz的信号。

但是,实际在用的时候,延时13us往往不够,难以读取到有效的数据(当然,读者可以自行测试,也就修改一个延时函数的功夫),这里把延时改为14us,如果读取的数据仍旧存在问题,如突然跳到一个特别大的值,然后又突然回归正常等,我们可以适当增加延时。对应到代码上的话,我们可以通过微调Delay14us内的i的值,来增大延时,下面是发送TX与RX的引脚定义与发送超声波的代码:

sbit TX=P1^0;
sbit RX=P1^1;

void Delay14us(void)    //@11.0592MHz
{
    unsigned char data i;

    _nop_();
    i = 45;//11.0592MHz的单片机,使用stc生成14us延时时,i等于36,这里稍微增大了i让延时增大
    while (--i);
}


void send_wave(void)//发送超声波的函数
{
    unsigned char i=0;
    for(i=0;i<8;i++)//发送8个40KHz的超声波
    {
        TX=1;Delay14us();
        TX=0;Delay14us();
    }    
}

2.单片机知识补充:定时器

在进行第二步:计时之前,需要先“科普”一下51系列单片机的基础知识。

单片机的数据手册可以通过新版的STC-ISP下载,或者点击下边的蓝字进行下载/查看

 STC15用户手册(点击查看或下载)

对于15单片机的定时器,也有许多,这里只挑对于超声波有用的寄存器讲。

这有一段STC-ISP生成的代码,系统频率为11.0592MHz,定时器1,定时长度为1ms,定时器模式为16位自动重,时钟频率为1T的定时器1的初始化代码

void Timer1_Init(void)        //1毫秒@11.0592MHz
{
    AUXR |= 0x40;            //定时器时钟1T模式
    TMOD &= 0x0F;            //设置定时器模式
    TL1 = 0xCD;                //设置定时初始值
    TH1 = 0xD4;                //设置定时初始值
    TF1 = 0;                //清除TF1标志
    TR1 = 1;                //定时器1开始计时
}

首先先解释一下为什么是定时器1,而不是定时器0呢,因为定时器0我们在比赛时百分百要用到,而其他定时器,或者说需不需要额外的定时器则是需要根据题目要求来判定,所以我们定时器0里面有我们需要运行的代码,我们可以开一个单独的定时器1来完成超声波,注意,超声波检查需要一个单独的定时器。

然后我们再看代码,第一行AUXR是辅助寄存器,用于控制时钟是1T还是12T,这个1T就是指不分频或者说1倍分频,12T就是指12分频。数据手册上也有,但是我们可以直接用STC-ISP来生成1T或者12T的代码,就不在赘述了。

对于第二行TMOD寄存器是用来控制定时器模式的,定时器主要有定时和计数功能。

定时就是我们经常使用的模式,计数器模式我们在NE555会用到。而在超声波测距时,我们恰恰是需要定时器去计时。

定时模式又有16位自动重载,16位自动重载,8位自动重载等模式(注意,不是所有8051系列单片机都有这些模式的),我们一般用16位自动重载,自动重载的意思就是,定时器溢出之后(定时器中断),定时器的TL和TH值会自动重新变为初始值,而不需要代码重置。

TL1 = 0xCD;                //设置定时初始值
TH1 = 0xD4;                //设置定时初始值

这两个就是刚才提到的TL和TH,这里是定时器1的TL和TH,所以是TL1和TH1,两个都是8位二进制数,合在一块刚好是16位二进制,其中,TH1是高八位,TL1是低八位,定时器开始计数时,他们就会自增1,直到溢出。根据它俩的初始值不同,我们可以设置出不同时间长度的定时器。

当定时器的TH1和TL1溢出时,TF1就会被置为1,同时引起定时器中断(当然前提是允许中断)。

当TR1=1时,定时器开始计时,TR1=0时,定时器停止计时。

综上,如果我们想用定时器1计时的话,就不能一开始就开启定时器计时,需要等我们发送完超声波之后再开始计时,也就是令TR1 =1,并且需要先将 TL1和TH1置为0,这样方便我们计算。等接收到信号之后,我们需要将TR1置为0停止计时,此时TH1和TL1存的数据,就是从开始计时到结束计时这一段的时间了,当然这个时间的单位可不是s呦。除此之外,如果长时间没有收到返回的超声波信号,一直等到TH1和TL1从0累加到溢出,也就是使TF1=1了,那说明没有检测到有效信号,我们也需要进行单独处理。

3.超声波的接收与计时

当发送完超声波之后,我们立即开始计时。当超声波接收端RX检测到信号时,会被置为1,也就是当检测到RX为1时,说明检测到超声波返回的信号了,我们也立刻停止计时,这段时间ultar_t就是超声波从发生到反射回来的时间,也就是超声波走了一个来回的时间。接下来,我们用代码来实现这个功能:

send_wave();
TR1=1;//开始计时
while((RX==1)&&(TF1==0));//计时的条件:没有收到反射回来的超声波,且定时器没有溢出
TR1=0;//停止计时
if(TF1==1)//定时器溢出,说明在一段时间内没收到反射回的超声波(代码/硬件/真的是因为距离太远都有可能导致没收到信号)
{
    TF1=0;
    ultar_t=0;//这个值可以随意设置,总之就是如果没有接收到反射回来的超声波时,要如何显示的值。。这里是接收到反射回来的超声波,就显示0
}
else//跳出那个while循环,不是因为TF1=1了,就是因为RX=0了,这个else的情况就是RX=0;也可以写成else if(RX==0);
{
    ultar_t=TH1;
    ultar_t<<=8;
    ultar_t|=TL1;
}

注意这里对TF1=1的情况也进行了处理,也就是没检测到信号的情况。

4.距离的计算

我们已经求得了时间ultar_t,但是这个时间的单位不是S也不是ms等,需要先进行转化成真正的时间才能用于距离的计算,关于距离的计算又涉及到了单片机的知识,这里根据单片机设置的频率不同和定时器时钟模式不同分开介绍。

1)定时器1为16位自动重载+1T@11.0592MHz

void Timer1_Init(void)        //@11.0592MHz
{
    AUXR |= 0x40;            //定时器时钟1T模式
    TMOD &= 0x0F;            //设置定时器模式
    TL1 = 0x00;                //设置定时初始值
    TH1 = 0x00;                //设置定时初始值
    TF1 = 0;                //清除TF1标志
    //TR1 = 1;                //定时器1开始计时
}

实际的时间=ultar_t*1/11059200(秒)

距离remote=ultar_t*1/11059200*340*100/2(厘米)

2)定时器1为16位自动重载+12T@11.0592MHz

void Timer1_Init(void)        //1微秒@11.0592MHz
{
    AUXR |= 0xBF;            //定时器时钟1T模式
    TMOD &= 0x0F;            //设置定时器模式
    TL1 = 0x00;                //设置定时初始值
    TH1 = 0x00;                //设置定时初始值
    TF1 = 0;                //清除TF1标志
    //TR1 = 1;                //定时器1开始计时
}

实际的时间=ultar_t*1/11059200*12(秒)

距离remote=ultar_t*1/11059200*12*340*100/2(厘米)

3)定时器1为16位自动重载+1T@12MHz

初始化函数与1)相同

实际的时间=ultar_t*1/12000000*12(秒)

距离remote=ultar_t*1/12000000*12*340*100/2(厘米)

4)定时器1为16位自动重载+12T@11.0592MHz

初始化函数与2)相同

实际的时间=ultar_t*1/12000000*12(秒)

距离remote=ultar_t*1/12000000*12*340*100/2(厘米)

总结

实际的距离等于ultar_t*1/主频*分频倍数*340*100/2(厘米)

对于1T和12T,12T就是十二分频,简单点说就是定时器记录的最长时间变为1T的十二倍,同时精度也变为原来的12倍,但是其实1T模式下,最远检测的距离等于65535*1/11059200*340*100/2≈100cm,100cm已经很远了,我用这个超声波时,测到70就已经很难了,所以一般情况下1T模式,而且不对超过100cm进行处理,也足够了。

对于主频率,比赛的时候,有时候会要求使用12MHz的主频(近几年来,可谓是大概率会这样要求),我们需要通过isp更改单片机内部的设置。关于主频的设置,我们只需在下载程序之前调一下IRC频率,下载程序之后会把单片机内部的设置一同更改了。我们也可以使用“检测MCU”选项,检测当前单片机内部的一些设置。

5.代码完善

与LED灯闪烁类似,我们并不期望无时无刻地发送与接收,而是每隔一定时间发送接受一次,再加上刚才关于距离计算的代码,以及每轮之后将相关数据清空,我们可以总结出超声波测距过程的代码:

if(count_ultar==1)
{
    count_ultar=0;
    send_wave();
    TR1=1;//开始计时
    while((RX==1)&&(TF1==0));//计时的条件:没有收到反射回来的超声波,且定时器没有溢出
    TR1=0;//停止计时
    if(TF1==1)//定时器溢出,说明在一段时间内没收到反射回的超声波(代码/硬件/真的是因为距离太远都有可能导致没收到信号)
    {
        TF1=0;
        ultar_t=0;//这个值可以随意设置,总之就是如果没有接收到反射回来的超声波时,要如何显示的值。。这里是接收到反射回来的超声波,就显示0
    }
    else//跳出那个while循环,不是因为TF1=1了,就是因为RX=0了,这个else的情况就是RX=0;也可以写成else if(RX==0);
    {
        ultar_t=TH1;
        ultar_t<<=8;
        ultar_t|=TL1;
    }

     remote=(unsigned int)(ultar_t*0.0015);//定时器1是1T。注意需要修改Timer1_Init()内的内容
    //remote=(unsigned int)(ultar_t*0.0184);//定时器1是12T
  
    ultar_t=0;
    TH1=0;
    TL1=0;
    }

    Delay100ms();
}

count_ultar为0时,200ms之后会被定时器置为1,也就是每200ms发送接收一次超声波。

unsigned int ultar_t=0;

bit count_ultar=0;

void Timer0_Isr(void) interrupt 1
{    
    if(count_ultar==0)
    {
        if(++count_200>200)
        {
            count_ultar=1;
            count_200=0;
        }

}

 三、完整代码展示

现在,完成以下功能对刚才介绍的功能进行演示:

1.单片机主频率设置为11.0592MHz

2.通过超声波读取距离信息,并显示到前三位数码管上,单位:CM

main.c

#include <stc15.h>
#include <intrins.h>


code unsigned char Seg_Table[] =
{
0xc0, //0
0xf9, //1
0xa4, //2
0xb0, //3
0x99, //4
0x92, //5
0x82, //6
0xf8, //7
0x80, //8
0x90, //9
0xFF
};

#define NIXIE_CHECK()	P2|=0xC0;P2&=0xDF;P2&=0x1F;
#define NIXIE_ON()	P2|=0xE0;P2&=0xFF;P2&=0x1F;

void Timer0_Init(void);		//1毫秒@11.0592MHz
void Delay100ms(void);	//@11.0592MHz
void send_wave(void);
void Timer1_Init(void);		//1毫秒@11.0592MHz

unsigned char location=0;
unsigned char Nixie_num[]={10,10,10,10,10,10,10,10};
bit count_ultar=0;//时间标志位
unsigned int remote=0;//距离信息
unsigned int ultar_t=0;//超声波往返的时间信息,注意必须用uint,不能用uchar
sbit TX=P1^0;//定义超声波的TX
sbit RX=P1^1;//定义超声波的RX
void main()
{
	Timer0_Init();
	Timer1_Init();
	EA=1;
	Delay100ms();
	while(1)
	{
		if(count_ultar==1)
		{
			count_ultar=0;
			send_wave();
			TR1=1;//开始计时
			while((RX==1)&&(TF1==0));//计时的条件:没有收到反射回来的超声波,且定时器没有溢出
			TR1=0;//停止计时
			if(TF1==1)//定时器溢出,说明在一段时间内没收到反射回的超声波(代码/硬件/真的是因为距离太远都有可能导致没收到信号)
			{
				TF1=0;
				ultar_t=0;//这个值可以随意设置,总之就是如果没有接收到反射回来的超声波时,要如何显示的值。。这里是接收到反射回来的超声波,就显示0
			}
			else//跳出那个while循环,不是因为TF1=1了,就是因为RX=0了,这个else的情况就是RX=0;也可以写成else if(RX==0);
			{
				ultar_t=TH1;
				ultar_t<<=8;
				ultar_t|=TL1;
			}
			remote=(unsigned int)(ultar_t*0.0015);//定时器1是1T。注意需要修改Timer1_Init()内的内容
			//remote=(unsigned int)(ultar_t*0.0184);//定时器1是12T
			ultar_t=0;
			TH1=0;
			TL1=0;
		}
		//数码管显示距离信息
		Nixie_num[0]=remote/100%10;
		Nixie_num[1]=remote/10%10;
		Nixie_num[2]=remote/1%10;

		Delay100ms();
	}
}

unsigned int count_200=0;
void Timer0_Isr(void) interrupt 1
{
	P0=0x01<<location;NIXIE_CHECK();
	P0=Seg_Table[Nixie_num[location]];NIXIE_ON();
	
	if(++location==8)
		location=0;
	
	if(count_ultar==0)//如果标志位为0
	{
		if(++count_200>200)//200ms后,将时间标志位置为1
		{
			count_ultar=1;
			count_200=0;
		}
	}
}

void Timer0_Init(void)		//1毫秒@11.0592MHz
{
	AUXR |= 0x80;			//定时器时钟1T模式
	TMOD &= 0xF0;			//设置定时器模式
	TL0 = 0xCD;				//设置定时初始值
	TH0 = 0xD4;				//设置定时初始值
	TF0 = 0;				//清除TF0标志
	TR0 = 1;				//定时器0开始计时
	ET0 = 1;				//使能定时器0中断
}
void Timer1_Init(void)		//1毫秒@11.0592MHz
{
	AUXR |= 0x40;			//定时器时钟1T模式
	//AUXR &= 0xBF;			//定时器时钟12T模式
	TMOD &= 0x0F;			//设置定时器模式
	TL1 = 0x00;				//设置定时初始值
	TH1 = 0x00;				//设置定时初始值
	TF1 = 0;					//清除TF1标志
	//TR1 = 1;				//定时器1开始计时
}

void Delay100ms(void)	//@11.0592MHz
{
	unsigned char data i, j, k;

	_nop_();
	_nop_();
	i = 5;
	j = 52;
	k = 195;
	do
	{
		do
		{
			while (--k);
		} while (--j);
	} while (--i);
}
void Delay14us(void)	//@11.0592MHz
{
	unsigned char data i;

	_nop_();
	i = 45;//11.0592MHz的单片机,使用stc生成14us延时时,i等于36,这里稍微增大了i让延时增大
	while (--i);
}


void send_wave(void)//发送超声波的函数
{
	unsigned char i=0;
	for(i=0;i<8;i++)//发送8个40KHz的超声波
	{
		TX=1;Delay14us();
		TX=0;Delay14us();
	}	
}

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

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

相关文章

express 定时删除 oss 垃圾图片

一&#xff1a; 删除垃圾图片 思路&#xff1a; 获取 oss 中存储的所有图片文件&#xff1b;获取数据库中存储的图片文件数据&#xff1b;对比差异&#xff0c;不在数据库中的 oss 图片文件即为要删除的垃圾图片。 实现&#xff1a; 1、获取所有 oss 文件 import OSS from…

Stable Diffusion之最全详解图解

Stable Diffusion之最全详解图解 1. Stable Diffusion介绍1.1 研究背景1.2 学术名词 2.Stable Diffusion原理解析2.1 技术架构2.2 原理介绍扩散过程 3.1 Diffusion前向过程3.2 Diffusion逆向&#xff08;推断&#xff09;过程 1. Stable Diffusion介绍 Stable Diffusion是2022…

蓝桥杯第七届电子类单片机组程序设计

目录 前言 蓝桥杯大赛历届真题 一、第七届比赛题&#xff1a; 二、功能实现&#xff1a; 1.基础/模板部分 2.菜单模式的切换 3.数码管闪烁功能 4.led灯闪烁部分 5.对按键的处理 5.对ds1302的处理 三、代码实现 main.c onewire.c onewire.h ds1302.c ds1302.h 前言 …

使用 Chainlit, Langchain 及 Elasticsearch 轻松实现对 PDF 文件的查询

在我之前的文章 “Elasticsearch&#xff1a;与多个 PDF 聊天 | LangChain Python 应用教程&#xff08;免费 LLMs 和嵌入&#xff09;” 里&#xff0c;我详述如何使用 Streamlit&#xff0c;Langchain, Elasticsearch 及 OpenAI 来针对 PDF 进行聊天。在今天的文章中&#xf…

PWR电源控制

PWR电源 PWR简介 PWR&#xff08;Power Control&#xff09;电源控制 PWR负责管理STM32内部的电源供电部分&#xff0c;可以实现可编程电压监测器和低功耗模式的功能 可编程电压监测器&#xff08;PVD&#xff09;可以监控VDD电源电压&#xff0c;当VDD下降到PVD阀值以下或上…

【安装指南】图床神器之Picgo下载、安装详细教程

&#x1f33c;一、概述 PicGo是一款开源的图片上传、管理工具&#xff0c;旨在帮助用户快速上传图片到云存储或图床&#xff0c;并提供链接方便在网页或其他应用中使用。它支持各种常见的图床服务商&#xff0c;如GitHub、七牛云、腾讯云等&#xff0c;并提供了简洁易用的界面和…

收藏:关于块存储,文件存储和对象存储

在B站上看到”【IT老齐465】“这个系列相当不错&#xff0c;每次的视频15分钟左右&#xff0c;出了400多个了&#xff0c;今天偶然看到&#xff0c;地址是&#xff1a;【IT老齐465】块存储、文件存储、对象存储的关系与区别_哔哩哔哩_bilibili 精彩摘录如下&#xff1a;

金庆培:世界需要有一个国际易货贸易协定

2024年2月4日&#xff0c;我拜访了中国金融与证券行业的著名专家徐士敏教授。 首先开言&#xff0c;他就告诉我&#xff1a;“你金老师&#xff0c;是研究中国自由贸易区创新发展和世界自由贸易区创新发展的专家&#xff0c;我认为&#xff0c;在当前世界经济形势下&#xff0c…

C语言每日一题(57)二叉树的最小深度

题目链接 力扣网111 二叉树的最小深度 题目描述 给定一个二叉树&#xff0c;找出其最小深度。 最小深度是从根节点到最近叶子节点的最短路径上的节点数量。 说明&#xff1a;叶子节点是指没有子节点的节点。 示例 1&#xff1a; 输入&#xff1a;root [3,9,20,null,null,…

四、案例 - Oracle数据迁移至MySQL

Oracle数据迁移至MySQL 一、生成测试数据表和数据1.在Oracle创建数据表和数据2.在MySQL创建数据表 二、生成模板文件1.模板文件内容2.模板文件参数详解2.1 全局设置2.2 数据读取&#xff08;Reader&#xff09;2.3 数据写入&#xff08;Writer&#xff09;2.4 性能设置 三、案例…

C++:priority_queue模拟实现

C&#xff1a;priority_queue模拟实现 什么是priority_queue模拟实现向上调整算法向下调整算法插入与删除 仿函数 什么是priority_queue priority_queue称为优先级队列。优先级队列是一种特殊的队列&#xff0c;其中每个元素都有一个相关的优先级。元素的优先级决定了它们在队…

Vue2中v-for 与 v-if 的优先级

在Vue2中&#xff0c;v-for 和 v-if 是常用的指令&#xff0c;它们在前端开发中非常有用。但是&#xff0c;当我们在同一个元素上同时使用这两个指令时&#xff0c;就需要注意它们的优先级关系了。 首先&#xff0c;让我们了解一下v-for和v-if的基本用法。 v-for 是Vue的内置…

基于JAVA的课程案例资源库系统 开源项目

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 管理员需求分析2.2 用户需求分析 三、系统设计3.1 业务流程设计3.1.1 管理员业务流程设计3.1.2 用户业务流程设计3.1.3 首页功能模块及业务流程分析3.1.4 案例资源中心功能模块及业务流程分析3.1.5 用户信息中心功能模块…

爱快使用VPN

文章目录 一、VPN服务器1. 各种VPN比较2. PPTP服务端配置3. 创建登录账号4. 创建端口映射5. 设置动态域名 二、Windows客户端1. 连接配置2. 不能连接 Internet 配置 一、VPN服务器 1. 各种VPN比较 PPTPIPSECOpenVPN简介微软推出的VPN协议&#xff0c;占用资源少更高级的VPN协…

再利用系统盘时,如何删除恢复分区(Recovery Partition)

系统盘有一个Recovery Partition&#xff0c;记录了重要的系统信息&#xff0c;不能删除。 Windows 10的 Disk Managment 不提供用户删除这个Partition的选项。 近日我插入一块原系统盘&#xff0c;Format后作为DataDisk&#xff0c;此时需要删除这块硬盘上的RecoveryPartition…

matplotlib画简单的论文图像

由于最近论文里需要插入绘图&#xff0c;因此写一篇博客记录一下 折曲线图 基本绘图 在matplotlib中折线和曲线图是最常用和最简单的图。只需要直接使用方法plot即可。 import matplotlib.pyplot as plt plt.plot(x,y) plt.show()其中上述的代码中x&#xff0c;y分别是横坐…

langchain==win11搭建使用GPU

annaconda安装Python 3.11.7 下载代码&#xff1a; GitHub - chatchat-space/Langchain-Chatchat: Langchain-Chatchat&#xff08;原Langchain-ChatGLM&#xff09;基于 Langchain 与 ChatGLM 等语言模型的本地知识库问答 | Langchain-Chatchat (formerly langchain-ChatGLM)…

【Tomcat】:One or more listeners failed to start.报错解决方案

报错信息:One or more listeners failed to start. Full details will be found in the appropriate container log file. 具体就是web.xml此配置报错: 服务器启动错误Tomcat:One or more listeners failed to start.报错解决方案 IDEA:在使用IDEA运行SSM项目的时候 , Tomcat运…

基于SSM的教材管理系统

文章目录 教材管理系统一、项目演示二、项目介绍三、系统部分功能截图四、部分代码展示五、底部获取项目源码&#xff08;9.9&#xffe5;&#xff09; 教材管理系统 一、项目演示 基于SSM的教材管理系统 二、项目介绍 有三个角色 1、管理员 功能模块&#xff1a;用户管理、教…

【数据结构】链表OJ面试题3《判断是否有环》(题库+解析)

1.前言 前五题在这http://t.csdnimg.cn/UeggB 后三题在这http://t.csdnimg.cn/gbohQ 记录每天的刷题&#xff0c;继续坚持&#xff01; 2.OJ题目训练 9. 给定一个链表&#xff0c;判断链表中是否有环。 力扣&#xff08;LeetCode&#xff09;官网 - 全球极客挚爱的技术成…