STM32第十六课:WiFi模块的配置及应用

文章目录

  • 需求
  • 一、WiFi模块概要
  • 二、配置流程
    • 1.配置通信串口,引脚和中断
    • 2.AT指令
    • 3.发送逻辑编写
  • 三、需求实现代码
  • 总结


需求

完成WiFi模块的配置,使其最终能和服务器相互发送消息。
在这里插入图片描述
在这里插入图片描述


一、WiFi模块概要

本次使用的WiFi模块为ESP-12F模块(安信可)
驱动芯片为ESP8266(乐鑫)。
ESP8266的使用:
1.作为mcu开发,再次搭建一下它的环境,开发周期较长。
2.利用官方固件使用AT指令(AT+**)开发。
在这里插入图片描述
芯片引脚:
在这里插入图片描述
通信接口:
在这里插入图片描述

二、配置流程

1.配置通信串口,引脚和中断

配置串口3(本次使用的WiFi模块串口接的是串口3)
默认配置: 波特率115200  8位数据位 0位校验位 1位停止位

配置PB10(TX) PB11(RX)
TX:复用推挽 RX:浮空输入

配置PE6(ESP模块的使能引脚)
高电平使能

void Esp8266_Config()
{
	  //开时钟:GPIOB,USART3
	  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
	  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOE,ENABLE);
	  RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3,ENABLE);
	
	    //配置对应的IO口 PB10(tx):复用推挽 PB11(RX):浮空输入
	    GPIO_InitTypeDef GPIO_InitStruct = {0};
		GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;
		GPIO_InitStruct.GPIO_Pin = GPIO_Pin_10;
		GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
		GPIO_Init(GPIOB,&GPIO_InitStruct);
		GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;
		GPIO_InitStruct.GPIO_Pin = GPIO_Pin_11;
		GPIO_Init(GPIOB,&GPIO_InitStruct);
		//PE6
		GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
		GPIO_InitStruct.GPIO_Pin = GPIO_Pin_6;
	    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
		GPIO_Init(GPIOE,&GPIO_InitStruct);
		
		
	    //配置串口3  8数据位,0校验位,1停止位,波特率115200
		USART_InitTypeDef USART_InitStruct = {0};//可以通过结构体类型跳转
		USART_InitStruct.USART_BaudRate = 115200;//波特率
		USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//硬件控制流不开
		USART_InitStruct.USART_Mode = USART_Mode_Tx|USART_Mode_Rx;//打开发送和接收
		USART_InitStruct.USART_Parity = USART_Parity_No;
		USART_InitStruct.USART_StopBits = USART_StopBits_1;
		USART_InitStruct.USART_WordLength = USART_WordLength_8b;
		USART_Init(USART3,&USART_InitStruct);
		USART_Cmd(USART3,ENABLE);
        //配置串口3的中断
		USART_ITConfig(USART3,USART_IT_RXNE,ENABLE);//USART1->CR1 |= 0x1<<5;//使能串口1的接收非空中断
		NVIC_SetPriority(USART3_IRQn,7);//设置优先级0~15
		NVIC_EnableIRQ(USART3_IRQn);//使能中断通道
		GPIO_SetBits(GPIOE,GPIO_Pin_6);
	    Delay_nms(500);
}

最后加的延时是为了保证所有配置都配置完。

2.AT指令

在配置中断的发送和接收前,我们要了解一下AT指令。
  AT 命令(AT Commands)最早是由发明拨号调制解调器(MODEM)的贺氏公司(Hayes)为了控制 MODEM 而发明的控制协议。后来随着网络带宽的升级,速度很低的拨号 MODEM 基本退出一般使用市场,但是 AT 命令保留下来。
  在嵌入式开发中,经常是使用AT命令去控制各种通讯模块,比如ESP8266 WIFI模块、4G模块、GPRS模块等等。一般就是主芯片通过硬件接口(比如串口、SPI)发送AT命令给通讯模块,模块接收到数据之后回应响应的数据。
AT指令的分类:
在这里插入图片描述
要注意:基本所有AT指令,结尾必须换行(\r\n)
ESP12F模块的工作模式:STA(连接热点) AP(释放热点) STA+AP
STA模式:模组作为节点去连接热点,然后就可连接某个服务器。
AP模式:模组作为热点,释放网络,可以在模组上创建服务器,其他设备连接他。
该模块的指令:
AT :测试固件的
AT+RST :重启ESP8266
ATE0 :关闭回显
ATE1 :打开回显
AT+CWMODE_DEF(_DEF有些固件支持,有些不支持)=x
x=1为设置工作模式 STA模式(可以连接其他设备热点),2为AP模式,3为组合模式。
AT+CWJAP_DEF=“WIFI名”,“WIFI密码” 连接WIFI。
AT+CWSAP :配置 ESP8266 SoftAP 参数(配置释放的热点) 。
AT+CIPAP :设置 ESP8266 SoftAP 的 IP 地址。
多连接情况下 (AT+CIPMUX=1),才能开启 TCP 服务器。
AT+CIPSERVER :建⽴ TCP 服务器
AT+CIPSTART=“TCP”,“IP”,端口号 以TCP的形式连接服务器
AT+CIPMODE=1 : 开启透传 (向wifi发送的所有消息(除+++外)都认为不是指令)
AT+CIPSEND : 启动发送功能
+++ 没有回车 : 退出透传
AT+CIPCLOSE : 退出服务器连接

连接服务器需要那些步骤:
1.连接网络
2.设置位连接热点模式:STA
3.连接热点:名字和密码
4.连接服务器 ip 和 端口
5.收发数据
AT是AT指令还是收发的数据
透传模式(透明传输,所有消息都认为是普通收发的消息)

3.发送逻辑编写

给串口发送命令,可以理解为发送字符串。
为了实现发送字符串,我们先写一个能发送单字节的函数

void Usart3Senddata(uint8_t data)
{
	//等待发送完成
	while(USART_GetFlagStatus(USART3,USART_FLAG_TC)==0);
	//如果上次发送完成,就发送
	USART_SendData(USART3,data);
}

由于字符串的末尾为"\0",我们结合单字节发送函数就能实现字符串的发送。

void U3_SendStr(uint8_t * data)
{
	while(*data!='\0')
	{
		Usart3Senddata(*data);
		data++;
	}
}

一个一个字符发送,发送一个data+1,遇到反斜杠0结束。
为了能够将发送的命令保存起来。
我们先定义一个结构体方便后续操作。

typedef struct{
	uint8_t recvbuf[1024];
	uint16_t recvcnt;//保存命令的条数,起到计数作用,防溢出。
}WIFIDATA;

在这里插入图片描述
此时我们写一个WiFi发送命令的函数 Wifi_Send_Cmd(char * cmd,char * recv,uint32_t timeout)
参数:命令,期待返回值,超时时间
之所以设置超时时间,是因为这些指令的执行都需要时间,反应较慢。

uint8_t Wifi_Send_Cmd(char * cmd,char * recv,uint32_t timeout)
{
	uint32_t timecnt=0;
	memset(&wifidata,0,sizeof(wifidata));//先清空
	U3_SendStr((uint8_t *)cmd);//发送命令
	while(strstr((char *)wifidata.recvbuf,recv)==NULL)
	{
	timecnt++;
	Delay_nms(1);
		if(timecnt>=timeout){
		printf("发送超时失败%s",cmd);
		return 1;
	 }
	}
	printf(" 发送成功 ");
	return 0;
}

其中strstr函数为查询目标字符串种是否有所需字符串,若有则返回所需字符串的地址,没有则返回0。
strstr(目标字符串,所需字符串)

然后就是写IP链接函数了

uint8_t Wifi_ConnectIP(void)
{
	if(Wifi_Send_Cmd("AT\r\n","OK",1000) != 0){//测试
		return 1;
	}
	if(Wifi_Send_Cmd("AT+CWMODE=1\r\n","OK",2000) != 0){//设置为STA
		return 1;
	}
	if(Wifi_Send_Cmd("AT+CWJAP=\"LEGION-5169\",\"88888888\"\r\n","OK",10000)!= 0){//连接热点
		return 1;
	}
	if(Wifi_Send_Cmd("AT+CIPSTART=\"TCP\",\"36.137.226.30\",37233\r\n","OK",10000)!= 0){//连接服务器
		return 1;
	}
	if(Wifi_Send_Cmd("AT+CIPMODE=1\r\n","OK",1000)!= 0){//开启透传
		return 1;
	}
	if(Wifi_Send_Cmd("AT+CIPSEND\r\n","OK",1000)!= 0){//启动发送功能
		return 1;
	}
	return 0;	
}

要注意:"需要反斜杠转义

最后编写串口3和串口1的中断函数:

void USART3_IRQHandler(void)
{
	
	uint8_t data=0;
	if((USART3->SR&0x1<<5)!=0)
	{//执行该中断函数的原因有很多,所以判断一下是不是接收导致的
		data = USART_ReceiveData(USART3);//读操作,同时也是清空中断标志位
		wifidata.recvbuf[wifidata.recvcnt] = data;
		wifidata.recvcnt++;
		wifidata.recvcnt%=1024;
		USART_SendData(USART1, data); 
	}
}
void USART1_IRQHandler(void)
{
	uint8_t data=0;
	if((USART1->SR&0x1<<5)!=0)
	{//执行该中断函数的原因有很多,所以判断一下是不是接收导致的
		//接收数据
		data = USART_ReceiveData(USART1);//读操作,同时也是清空中断标志位
     	USART3->DR = data;//发送数据
		//USART_SendData(USART5, data); 
	}
}

逻辑如下:
串口3先读接收到的数据,然后将数据保存到结构体中,结构体中的计数器++并对1024取余防止溢出,最后将数据发送给串口1。
串口1先读接收到的数据,后将数据发送给串口3。

三、需求实现代码

main.c

#include "stm32f10x.h"
#include "usart.h"
#include "stdio.h"
#include "delay.h"
#include "string.h"
#include "wifi.h"
uint8_t Send_wifidata[102];

int main()
{
	NVIC_SetPriorityGrouping(5);//两位抢占两位次级
    Usart1_Config(); 
	SysTick_Config(72000);
	Esp8266_Config();
    strcpy((char*)Send_wifidata, "hello world");
	Wifi_ConnectIP();
    U3_SendStr(Send_wifidata);
    while(1)
    {	
    }
		return 0;
}

WiFi.c

#include "wifi.h"

WIFIDATA wifidata={0};

//配置串口3  8数据位,0校验位,1停止位,波特率115200
//PB10(TX) PB11(RX)
void Esp8266_Config()
{
	 //开时钟:GPIOB,USART3
	  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
	  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOE,ENABLE);
	  RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3,ENABLE);
	
	  //配置对应的IO口 PB10(tx):复用推挽 PB11(RX):浮空输入
	  GPIO_InitTypeDef GPIO_InitStruct = {0};
		GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;
		GPIO_InitStruct.GPIO_Pin = GPIO_Pin_10;
		GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
		GPIO_Init(GPIOB,&GPIO_InitStruct);
		GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;
		GPIO_InitStruct.GPIO_Pin = GPIO_Pin_11;
		GPIO_Init(GPIOB,&GPIO_InitStruct);
		//PE6
		GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
		GPIO_InitStruct.GPIO_Pin = GPIO_Pin_6;
	    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
		GPIO_Init(GPIOE,&GPIO_InitStruct);
		
		
	  //配置串口3  8数据位,0校验位,1停止位,波特率115200
		USART_InitTypeDef USART_InitStruct = {0};//可以通过结构体类型跳转
		USART_InitStruct.USART_BaudRate = 115200;//波特率
		USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//硬件控制流不开
		USART_InitStruct.USART_Mode = USART_Mode_Tx|USART_Mode_Rx;//打开发送和接收
		USART_InitStruct.USART_Parity = USART_Parity_No;
		USART_InitStruct.USART_StopBits = USART_StopBits_1;
		USART_InitStruct.USART_WordLength = USART_WordLength_8b;
		USART_Init(USART3,&USART_InitStruct);
		USART_Cmd(USART3,ENABLE);
    //配置串口3的中断
		USART_ITConfig(USART3,USART_IT_RXNE,ENABLE);//USART1->CR1 |= 0x1<<5;//使能串口1的接收非空中断
		NVIC_SetPriority(USART3_IRQn,7);//设置优先级0~15
		NVIC_EnableIRQ(USART3_IRQn);//使能中断通道
		GPIO_SetBits(GPIOE,GPIO_Pin_6);
	    Delay_nms(500);
}

void USART3_IRQHandler(void)
{
	
	uint8_t data=0;
	if((USART3->SR&0x1<<5)!=0)
	{//执行该中断函数的原因有很多,所以判断一下是不是接收导致的
		data = USART_ReceiveData(USART3);//读操作,同时也是清空中断标志位
		wifidata.recvbuf[wifidata.recvcnt] = data;
		wifidata.recvcnt++;
		wifidata.recvcnt%=1024;
		USART_SendData(USART1, data); 
	}
}

//串口5发送单字节函数
void Usart3Senddata(uint8_t data)
{
	//等待发送完成
	while(USART_GetFlagStatus(USART3,USART_FLAG_TC)==0);
	//如果上次发送完成,就发送
	USART_SendData(USART3,data);
}

//串口5发送数组函数
void U3_Sendarr(uint8_t * data,uint32_t len)
{
	uint32_t i=0;
	for(i=0;i<len;i++){
		Usart3Senddata(*data);
		data++;
	}
}

void U3_SendStr(uint8_t * data)
{	
	while(*data!='\0')
	{
		Usart3Senddata(*data);
		data++;
	}
}

uint8_t Wifi_Send_Cmd(char * cmd,char * recv,uint32_t timeout)
{
	uint32_t timecnt=0;
	memset(&wifidata,0,sizeof(wifidata));
	U3_SendStr((uint8_t *)cmd);
	while(strstr((char *)wifidata.recvbuf,recv)==NULL){
	timecnt++;
	Delay_nms(1);
		if(timecnt>=timeout){
		printf("发送超时失败%s",cmd);
		return 1;
	 }
	}
	printf(" 发送成功 ");
	return 0;
}

uint8_t Wifi_ConnectIP(void)
{
	if(Wifi_Send_Cmd("AT\r\n","OK",1000) != 0){//测试
		return 1;
	}
	if(Wifi_Send_Cmd("AT+CWMODE=1\r\n","OK",2000) != 0){//设置为STA
		return 1;
	}
	if(Wifi_Send_Cmd("AT+CWJAP=\"LEGION-5169\",\"88888888\"\r\n","OK",10000)!= 0){//连接热点
		return 1;
	}
	if(Wifi_Send_Cmd("AT+CIPSTART=\"TCP\",\"36.137.226.30\",37233\r\n","OK",10000)!= 0){//连接服务器
		return 1;
	}
	if(Wifi_Send_Cmd("AT+CIPMODE=1\r\n","OK",1000)!= 0){//开启透传
		return 1;
	}
	if(Wifi_Send_Cmd("AT+CIPSEND\r\n","OK",1000)!= 0){//启动发送功能
		return 1;
	}
	return 0;	
}

wifi.h

#ifndef _WIFI_H_
#define _WIFI_H_
#include "stm32f10x.h"
#include "delay.h"
#include "stdio.h"
#include "string.h"
typedef struct{
	uint8_t recvbuf[1024];
	uint16_t recvcnt;
}WIFIDATA;

void Esp8266_Config();
void U3_SendStr(uint8_t * data);
uint8_t Wifi_Send_Cmd(char * cmd,char * recv,uint32_t timeout);
uint8_t Wifi_ConnectIP(void);
void U3_SendStr(uint8_t * data);
#endif
		

总结

1.先看原理图,配串口,引脚和中断。
2.根据发送逻辑进行中断函数的编写。
3.在主函数中调用并按照需求进行实现。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

聚类分析方法(一)

目录 一、聚类分析原理&#xff08;一&#xff09;聚类分析概述&#xff08;二&#xff09;聚类的数学定义&#xff08;三&#xff09;簇的常见类型&#xff08;四&#xff09;聚类框架及性能要求&#xff08;五&#xff09;簇的距离 二、划分聚类算法&#xff08;一&#xff0…

车载测试之-CANoe创建仿真工程

在现代汽车工业中&#xff0c;车载测试是确保车辆电子系统可靠性和功能性的关键环节。而使用CANoe创建仿真工程&#xff0c;不仅能够模拟真实的车辆环境&#xff0c;还能大大提升测试效率和准确性。那么&#xff0c;CANoe是如何实现这些的呢&#xff1f; 车载测试中&#xff0…

PXIe-7976【K410T】

起售价 RMB 152,880.00 块RAM(BRAM): 28620 kbit 动态RAM(DRAM): 2 GB FPGA: Kintex-7 410T PXI背板链路: PCI-Express Gen2 x 8 FPGA片: 63550 DSP片: 1540

敏感词匹配DFA算法

算法简介与场景介绍 DFA算法&#xff0c;中文全称为确定性有穷自动机。它的基本思想是构建一个有穷自动机&#xff0c;当用户输入文本时&#xff0c;通过自动机的状态转换来快速匹配敏感词。具体特征是&#xff0c;有一个有效状态的集合和一些从一个状态通向另一个状态的边&am…

并发处理 优先图和多重图

优先图(Precedence Graph)视图可串性多重图(Polygraph) 优先图(Precedence Graph) 优先图用于冲突可串性的判断。 优先图结构&#xff1a; 结点 (Node)&#xff1a;事务&#xff1b;有向边 (Arc): Ti → Tj &#xff0c;满足 Ti <s Tj&#xff1b; 存在Ti中的操作A1和Tj…

利用redis Zset实现 排行榜功能 配合xxl-job持久化每一个赛季的排行榜

zset 可以排序 使用xxl-job实现定时任务 对历史排行榜持久化到数据库 排行榜有当前赛季排行版和历史排行榜 当前赛季排行榜利用redis 中的SortSet 数据结构 获取 每个月的 月初 利用xxl-job的定时任务持久化化上一个月的排行榜信息 并删除redis中的数据 当排行榜数据量巨大时…

【5G VoNR】VoNR流程简述

博主未授权任何人或组织机构转载博主任何原创文章&#xff0c;感谢各位对原创的支持&#xff01; 博主链接 本人就职于国际知名终端厂商&#xff0c;负责modem芯片研发。 在5G早期负责终端数据业务层、核心网相关的开发工作&#xff0c;目前牵头6G技术研究。 博客内容主要围绕…

移动校园(5):课程表数据获取及展示

首先写下静态页面&#xff0c;起初打算做成一周的课表&#xff0c;由于是以小程序的形式展现&#xff0c;所以显示一周的话会很拥挤&#xff0c;所以放弃下面的方案&#xff0c;改作一次显示一天 改后结果如下&#xff0c;后期还会进行外观优化 真正困难的部分是数据获取 大家大…

拆分Transformer注意力,韩国团队让大模型解码提速20倍|大模型AI应用开始小规模稳步爆发|周伯文:大模型也有幻觉,全球AI创新指数公布

拆分Transformer注意力&#xff0c;韩国团队让大模型解码提速20倍AI正在颠覆AI上市不到两年&#xff0c;蜗牛游戏可能要退市了&#xff1f;世界人工智能大会结束了&#xff0c;百花齐放&#xff0c;但也群魔乱舞“串联OLED”被苹果带火了&#xff0c;比OLED强在哪里&#xff1f…

文化财经macd顶底背离幅图指标公式源码

DIFF:EMA(CLOSE,12) - EMA(CLOSE,26); DEA:EMA(DIFF,9); MACD:2*(DIFF-DEA),COLORSTICK; JC:CROSS(DIFF,DEA); SC:CROSSDOWN(DIFF,DEA); N1:BARSLAST(JC)1; N2:BARSLAST(SC)1; HH:VALUEWHEN(CROSSDOWN(DIFF,DEA),HHV(H,N1));//上次MACD红柱期间合约最大值 HH2:VALUEWHE…

MySQL:视图、用户管理、C/C++/图形化界面链接访问数据库、网页逻辑

文章目录 1.视图1.1 视图的基本使用1.2 视图的基本规则 2.用户管理2.1 创建、删除、修改用户2.2 数据库权限 3.C/C/图形化界面链接访问数据库3.1 准备工作及常用接口介绍3.2 图形化界面访问MySQL 4.用户逻辑(注册&&登录) 1.视图 视图是一个虚拟表&#xff0c;其内容由…

springboot苏桦旅游管理系统-计算机毕业设计源码02123

摘要 旅游业在全球范围内不断发展&#xff0c;为了提供高效的旅游管理和服务&#xff0c;开发一个旅游管理系统具有重要意义。本文旨在设计和实现该旅游管理系统&#xff0c;以满足用户和管理员的需求。该系统采用Spring Boot作为后端框架&#xff0c;利用其简化的开发流程和强…

ComfyUI如何高效率使用多Lora

Efficient 工作流 {"last_node_id": 29,"last_link_id": 56,"nodes": [{"id": 26,"type": "LoRA Stacker","pos": [540,270],"size": {"0": 320,"1": 322},"flag…

如何让代码兼容 Python 2 和 Python 3?Future 库助你一臂之力

目录 01Future 是什么? 为什么选择 Future? 安装与配置 02Future 的基本用法 1、兼容 print 函数 2、兼容整数除法 3、兼容 Unicode 字符串 03Future 的高级功能 1. 处理字符串与字节 2. 统一异常处理…

STM32-TIM定时器

本内容基于江协科技STM32视频内容&#xff0c;整理而得。 文章目录 1. TIM1.1 TIM定时器1.2 定时器类型1.3 基本定时器1.4 通用定时器1.4 高级定时器1.5 定时中断基本结构1.6 预分频器时序1.7 计数器时序1.8 计数器无预装时序1.9 计数器有预装时序1.10 RCC时钟树 2. TIM库函数…

路径跟踪算法之PID、PP、Stanley详细理解

一、前言 今天又来补作业了&#xff01; 在跟踪控制领域&#xff0c;PID&#xff08;Proportional-Integral-Derivative, 分别为比例、积分、微分&#xff09;、PP&#xff08; Pure-Puresuit, 纯跟踪&#xff09;、Stanley&#xff08;前轮反馈控制&#xff09;是三种最为常见…

02STM32软件安装新建工程

STM32软件安装&新建工程 1.软件安装&#xff1a;1.1Keil5 MDK安装1.2安装器件支持包离线安装支持包在线安装支持包 1.3软件注册&#xff1a;1.4安装驱动STLINK驱动JLink驱动在此文件夹下USB转串口 2开发方式&新建工程步骤&架构2.1STM32开发方式&#xff1a;库函数压…

线性系统理论及应用GUI设计及仿真

目录 1.控制系统的状态空间模型 1.1.状态空间模型 1.2 传递函数模型 1.3 传递函数转换为状态空间模型 1.4.状态空间模型转换为传递函数 1.5.状态空间模型转化为约当标准型 2.线性系统的时域分析 2.1.矩阵指数函数的计算 2.2.线型定常连续系统的状态空间模型求解 3.线…

《Nature》文章:ChatGPT帮助我学术写作的三种方式

图片翻译 ** 文章内容** 忏悔时间&#xff1a;我使用生成式人工智能&#xff08;AI&#xff09;。尽管在学术界关于聊天机器人是积极力量还是消极力量的争论不休&#xff0c;但我几乎每天都使用这些工具来完善我所写论文中的措辞&#xff0c;并寻求对我被要求评估的工作进行替…

Mysql-常用函数及其用法总结

1、字符串函数 测试用例如下&#xff1a; 1.1 CONCAT() 将多个字符串连接成一个字符串。 SELECT CONCAT(first_name, , last_name) AS full_name FROM users; -- 期望结果&#xff1a;John Doe, Jane Smith, Michael Johnson 1.2 SUBSTRING() 提取子字符串 SELECT SUBSTR…