当前位置: 首页 > news >正文

第31章_瑞萨MCU零基础入门系列教程之WIFI蓝牙模块驱动实验

本教程基于韦东山百问网出的 DShanMCU-RA6M5开发板 进行编写,需要的同学可以在这里获取: https://item.taobao.com/item.htm?id=728461040949

配套资料获取:https://renesas-docs.100ask.net

瑞萨MCU零基础入门系列教程汇总: https://blog.csdn.net/qq_35181236/article/details/132779862


第31章 WIFI&蓝牙模块驱动实验

本章目标

  • 了解WiFi蓝牙芯片W800的通信协议;
  • 学会使用串口收发AT指令实现开发板联网;

31.1 W800出厂固件烧写

烧写W800的固件时,需要使用X/Y modem串口协议。因而需要支持X/Y modem协议的串口工具,本书使用Xshell。

31.1.1 Xshell软件安装

免费体验版的Xshell下载入口地址是:

https://www.xshell.com/zh/free-for-home-school/

进入网站填写信息后,Xshell官方会将免费下载链接下发到填写的邮箱:

将邮箱中的链接复制粘贴到浏览器即可进行下载,下载安装。

31.1.2 Xshell的使用

Xshell安装好之后,双击运行软件,在弹出的会话窗口点击“新建”添加串口连接:

然后在“连接”项中将协议设置为“Serial”,如下图所示:

接着去“连接”中的“串口”处选择串口号(下图的COM20只是一个例子,按照下一节的说明使用USB串口连接W800后,端口好可能不一样)和设置通信参数,如图所示:

设置好之后点击右下角的“连接”即可连接指定串口设备了。

31.1.3 硬件连接

W800的硬件接口图如下:

按上图将硬件连接好之后,使用Xshell打开串口,随后再将上图的WIFI_RESET引脚短接GND复位W800,之后W800就会一直往Xshell发送字符’C’,如下图所示:

进入此状态后,就可以按照下节内容烧写固件了。

31.1.4 固件烧写步骤

W800进入烧写状态后,在Xshell的显示窗口点击鼠标右键,选择“传输”->“YMODEM(Y)”->“用YMODEM发送(S)”,如下图所示:

随后进入资料包中固件所在位置,选择w800.fls开始烧写:

最后等待传输烧写完毕:

烧写完成后W800会继续对外输出’C’表示还在烧写状态,此时将BOOT引脚与GND断开,并且再次手动复位W800,就可以让W800进入正常工作状态,如下图所示:

当Xshell打印出“user task”后,就表明W800已经进入工作状态了。此时可以在Xshell窗口直接输入指令“AT+”并车键(Xshell不会显示输入指令),如果返回“+OK”固件烧写成功了,如下图所示:

31.2 AT+指令简介

W800的固件AT+指令,在官方手册《WM_W800_SDK_AT指令用户手册.pdf》中有详细描述,这个文档已经放到本书的配套资料中。

W800支持的AT+指令非常的多,本书仅展现几个常用的指令。

指令指令格式响应说明
AT+“AT+\r\n”“+OK\r\n\r\n”测试指令,响应+OK表示测试成功
AT+Z“AT+Z\r\n”“+OK\r\n\r\n”软件复位,响应+OK表示复位指令发送成功,复位后会响应“user task”
AT+E“AT+E\r\n”“+OK\r\n\r\n”回显切换指令,输入一次切换一次状态,在回显状态下W800会连带指令和结果一起响应
AT+RSTF“AT+RSTF\r\n”“+OK\r\n\r\n”恢复FLASH中的出厂设置。恢复后的设置需系统重启后才能生效
AT+WPRT“AT+WPRT=[!?][type]\r\n”例如:AT+WPRT=0\r\n“+OK[=type]\r\n\r\n”例如:+OK=0\r\n\r\n设置 /查询无线网络类型:l 0:STA;l 2:SoftAP;l 3:APSTA
AT+WSCAN“AT+WSCAN\r\n”“+OK=……”该指令仅在无线网络类型为 STA时有效,用于扫描无线网络,完成后返回。
AT+SSID“AT+SSID=[!?][SSID]”例如:AT+SSID=100ask\r\n“+OK[=ssid]\r\n”设置 /查询无线网络名称,即 SSID
AT+KEY“AT+KEY=[!?][format],[index],[key]\r\n”“+OK[=format,index,key]\r\n”设置 /查询网络密钥
AT+WJOIN“AT+WJOIN\r\n”“+OK=&ltbssid&gt,&lttype&gt…\r\n”如果当前网络类型为为 STA 时,本指令功能为连接 AP。如果当前网络类型SoftAP或者APSTA 时,本指令功能为创建
AT+WLEAV“AT+WLEAV\r\n”“+OK\r\n\r\n”无线网络类型为 STA时,用于断开当前无线网络。
AT+NIP“AT+NIP=[!?][type]…\r\n”“+OK[=type]…\r\n”当 无线网卡作为STA时,该指令用于设置 /查询本端 IP地址。
AT+LKSTT“AT+LKSTT\r\n”+OK[=status,ip,netmask,gateway,dns1,dns2]\r\n\r\n查询本端网络连接状态
AT+SKCT“AT+SKCT=[protocol]…\r\n”“+OK=&ltsocket&gt\r\n”建立 socket。在 client模式,等待连接完成(成功或失败)后返回;在 server模式下,创建完成后直接返回。
AT+SKCLS“AT+SKCLS=&ltsocket&gt\r\n”“+OK\r\n\r\n”关闭指定的socket。
AT+SKSND“AT+SKSND=[socket],[size]\r\n”“+OK\r\n\r\n”响应OK后发送数据流
AT+SKRCV“AT+SKRCV=[socket],[size]\r\n”“+OK\r\n\r\n”响应OK后会将rxdata中的数据发送到串口

31.3 模块配置

本实验只使用到UART,请参考前文的操作在FSP中配置UART及其引脚。本次实验仅展示配置结果。

31.3.1 硬件连接

板载W800的原理图如下图所示:

使用到的RA6M5处理器引脚是P505和P506,对应于SCI的UART6的TX/RX引脚。

31.3.2 UART模块配置

  1. UART6

  1. UART7

31.4 驱动程序

31.4.1 设备对象封装

在《30.3设备对象封装》的基础上,对定时器的设备对象进行了改进,添加了一个Timeout函数,它实现了延时函数。代码如下:

static struct TimerDev gSystickDevice = {.name = "Systick",.channel = 0xFF,.status = 0,.Init = SystickInit,.Start = NULL,.Stop = NULL,.Read = NULL,.Timeout = HAL_Delay,.next = NULL
};
void SystickTimerDevicesCreate(void)
{TimerDeviceInsert(&gSystickDevice);gSystickDevice.Init(&gSystickDevice);
}
static int HAL_Delay(struct TimerDev *ptdev, uint32_t timeout)
{if(NULL == ptdev)       return -EINVAL;if(0 == ptdev->status)  return -EIO;uint32_t dwStart = dwTick;uint32_t dwWait = timeout;/* Add a freq to guarantee minimum wait */if (dwWait < HAL_MAX_DELAY){dwWait += (uint32_t)(1);}while((dwTick - dwStart) < dwWait){}return ESUCCESS;
}

31.4.2 初始化UART

本次实验会用到两个UART:

  • UART6:和WiFi蓝牙芯片W800通信;
  • UART7:调试打印;

对于UART6,由于W800每次收发的数据长度是不确定的,不适合用DTC或者DMA来辅助传输数据,因而使用环形缓冲区。在初始化UART6的时候申请了一个环形缓冲区,而UART7作为调试串口,没有使用环形缓冲区。代码如下:

static struct RingBuffer *gWiFiBuffer = NULL;
static struct UartDev gWiFiDevice = {.name = "WiFi Uart",.channel = 6,.Init = UARTDrvInit,.Read = UARTDrvRead,.Write = UARTDrvWrite,.next = NULL
};
void UartDevicesCreate(void)
{UartDeviceInsert(&gLogDevice);UartDeviceInsert(&gWiFiDevice);gLogDevice.Init(&gLogDevice);
}
static int UARTDrvInit(struct UartDev *ptdev)
{if(NULL == ptdev)   return -EINVAL;switch(ptdev->channel){case 0:case 1:case 2:case 3:case 4:case 5:case 6:{fsp_err_t err = g_uart6.p_api->open(g_uart6.p_ctrl, g_uart6.p_cfg);assert(FSP_SUCCESS == err);gWiFiBuffer = RingBufferNew(1024);assert(NULL != gWiFiBuffer);break;}case 7:{fsp_err_t err = g_uart7.p_api->open(g_uart7.p_ctrl, g_uart7.p_cfg);assert(FSP_SUCCESS == err);break;}case 8:case 9:break;default:break;}return ESUCCESS;
}

31.4.3 中断回调函数

对于UART6和UART7,需要提供中断回调函数。它们的操作是类似的,UART6需要处理“发送完成”和“接收完成”两种情况,而UART7只需要处理“发送完成”。UART6的中断回调函数代码如下:

void uart6_callback(uart_callback_args_t * p_args)
{switch(p_args->event){case UART_EVENT_RX_COMPLETE:{break;}case UART_EVENT_TX_COMPLETE:{gUart6TxCplt = true;break;}case UART_EVENT_RX_CHAR:{gWiFiBuffer->Write(gWiFiBuffer, (unsigned char*)&p_args->data, 1);break;}case UART_EVENT_ERR_PARITY:case UART_EVENT_ERR_FRAMING:case UART_EVENT_ERR_OVERFLOW:case UART_EVENT_BREAK_DETECT:case UART_EVENT_TX_DATA_EMPTY:break;default:break;   }
}
  • 第11行:将UART6的发送完成标志写为true;
  • 第16行:如果接收到数据,将它写入到UART6的缓冲区中;

31.4.4 UART发送函数

UART设备的发送函数比较简单,调用UART6和UART7设备结构体的write函数即可:

static int UARTDrvWrite(struct UartDev *ptdev, unsigned char * const buf, unsigned int length)
{if(NULL == ptdev)   return -EINVAL;if(NULL == buf)     return -EINVAL;if(0 == length)     return -EINVAL;switch(ptdev->channel){case 0:case 1:case 2:case 3:case 4:case 5:case 6:{fsp_err_t err = g_uart6.p_api->write(g_uart6.p_ctrl, buf, length);assert(FSP_SUCCESS == err);UART6WaitTxCplt();break;}case 7:{fsp_err_t err = g_uart7.p_api->write(g_uart7.p_ctrl, buf, length);assert(FSP_SUCCESS == err);UART7WaitTxCplt();break;}case 8:case 9:break;default:break;}return ESUCCESS;
}

31.4.5 UART数据读取函数

对于UART6,读取UART的数据时,是从它的环形缓冲区里读取数据。对于UART7,调用FSP封装的函数读数据。代码如下:

static int UARTDrvRead(struct UartDev *ptdev, unsigned char *buf, unsigned int length)
{if(NULL == ptdev)   return -EINVAL;if(NULL == buf)     return -EINVAL;if(0 == length)     return -EINVAL;switch(ptdev->channel){case 0:case 1:case 2:case 3:case 4:case 5:case 6:{if(gWiFiBuffer->Read(gWiFiBuffer, buf, length) != length)return -EIO;break;}case 7:{fsp_err_t err = g_uart7.p_api->read(g_uart7.p_ctrl, buf, length);assert(FSP_SUCCESS == err);UART7WaitRxCplt();break;}case 8:case 9:break;default:break;}return (int)length;
}

31.5 W800 WiFi蓝牙模块

WiFi蓝牙模块的设备驱动代码在Devices/wifi_bluetooth文件夹中,文件dev_wifi_bt.c/.h。

31.5.1 初始化W800

要初始化W800,实际是初始化W800用到的串口设备,即初始化UART6:

static UartDevice *pWiFiBtDev = NULL;
int WiFiBtDevInit(void)
{pWiFiBtDev = UartDeviceFind("WiFi");if(NULL==pWiFiBtDev) return -ENXIO;if(pWiFiBtDev->Init(pWiFiBtDev) != ESUCCESS)    return -EIO;return ESUCCESS;
}

31.5.2 AT+指令返回判定

在AT+指令简介中已经直到,每个AT+指令都有其对应的响应。对于大多数指令,只需要判断它收到的回应是不是“+OK”即可。将这个功能封装为如下函数:

static int WiFiBtDevCmdRet(const char *ret)
{unsigned short timeout = 3000;unsigned char i = 0;unsigned char buf[128] = {0};while(timeout){if(strstr((char*)buf, ret)){return ESUCCESS;}else if(strstr((char*)buf, "+ERR")){return -EIO;}else if(pWiFiBtDev->Read(pWiFiBtDev, &buf[i], 1)==1){printf("%c", buf[i]);i++;}mdelay(1);timeout--;}return -EIO;
}
  • 第08行:如果接收的数据中读出来有指定响应字符串,就返回ESUCCESS;
  • 第12行:如果响应中有“+ERR”就表示指令失败,返回-EIO;
  • 第16行:如果想要的响应或者错误响应都没有,则继续从缓冲区中读取数据并打印出来;
  • 第21~22行:超时等待倒计时,如果是超时退出则返回-EIO;

31.5.3 设置W800的工作模式

W800的WiFi功能有3个工作模式:STA、SoftAP和APSTA,定义了一个枚举类型:

typedef enum{STA     = 0,SoftAP  = 2,APSTA   = 3
}WorkType;

发出指令“AT+WPRT=&lttype&gt\r\n”(注意必须要有’\r’),就可以设置W800的工作模式。具体的实现很简单,调用UART设备的write函数发送这个字符串,然后等待响应即可。代码如下:

int WiFiBtDevSetWorkType(WorkType type)
{char str[64];sprintf(str, "AT+WPRT=%d\r\n", type);if(pWiFiBtDev->Write(pWiFiBtDev, (unsigned char*)str, strlen(str)) != ESUCCESS)return -EIO;mdelay(100);return WiFiBtDevCmdRet("+OK");
}

31.5.4 设置W800的DHCP状态

如果想让W800在连接上了热点之后自动获得IP地址,则需要使能W800的DHCP功能;如果想要固定W800的IP地址,则需要关闭DHCP自动分配功能,并且指定IP。

使能DHCP功能的指令是“AT+NIP=0\r\n”,代码如下:

int WiFiBtDevEnableDHCP(void)
{int ret = -EIO;char *str = "AT+NIP=0\r\n";ret = pWiFiBtDev->Write(pWiFiBtDev, (unsigned char*)str, strlen(str));if(ESUCCESS!= ret) return ret;mdelay(100);ret = WiFiBtDevCmdRet("+OK");return ret;
}

要手工设置IP,使用指令“AT+NIP=1,[IP],[net_mask],[gate_way]\r\n”,它会关闭DHCP功能、指定IP、子网掩码和默认网关。代码如下:

int WiFiBtDevDisableDHCP(const char *ip, const char *netmask, const char *gateway)
{int ret = -EIO;char str[64];sprintf(str, "AT+NIP=1,%s,%s,%s\r\n", ip, netmask, gateway);ret = pWiFiBtDev->Write(pWiFiBtDev, (unsigned char*)str, strlen(str));if(ESUCCESS!= ret) return ret;mdelay(100);ret = WiFiBtDevCmdRet("+OK");return ret;
}

31.5.5 连接指定热点

连接热点需要3个指令:

  • 设置热点的名称:AT+SSID=[SSID]\r\n;
  • 设置连接热点的密码:AT+KEY=[密钥格式],[密钥索引号],[密钥字符串]\r\n;
  • 连接热点:AT+WJOIN\r\n;

对于设置密钥的参数,手册中是这样解释的:

  • 密钥格式:0-Hex格式;1-ASCII格式;
  • 密钥索引号:1~4用于WEP加密密钥,其它加密方式固定为0;
  • 密钥字符串:以双引号包围,根据不同的安全模式,密钥使用的长度与格式要求定义如下:

本实验将连接热点的三个指令封装到一个函数中,调用者只需要传入连接热点的名称和密码即可:

int WiFiBtDevConnectWiFi(const char *name, const char *password)
{int ret = -EIO;char ssid[32];sprintf(ssid, "AT+SSID=%s\r\n", name);ret = pWiFiBtDev->Write(pWiFiBtDev, (unsigned char*)ssid, strlen(ssid));if(ESUCCESS != ret) return ret;mdelay(100);ret = WiFiBtDevCmdRet("+OK");if(ESUCCESS != ret) return ret;char key[32] ;sprintf(key, "AT+KEY=1,0,%s\r\n", password);ret = pWiFiBtDev->Write(pWiFiBtDev, (unsigned char*)key, strlen(key));if(ESUCCESS != ret) return ret;mdelay(100);ret = WiFiBtDevCmdRet("+OK");if(ESUCCESS != ret) return ret;char join[32] = "AT+WJOIN\r\n";ret = pWiFiBtDev->Write(pWiFiBtDev, (unsigned char*)join, strlen(join));if(ESUCCESS != ret) return ret;mdelay(100);ret = WiFiBtDevCmdRet("+OK");int ret1 = WiFiBtDevCmdRet("\r\n\r\n");if(ret==ESUCCESS && ret1)mdelay(1000);return ret;
}

31.5.6 断开热点连接

断开热点的连接,只需要发送指令“AT+WLEAV\r\n”即可,代码如下:

int WiFiBtDevDisconnectWiFi(void)
{int ret = -EIO;char *leavw = "AT+WLEAV\r\n";ret = pWiFiBtDev->Write(pWiFiBtDev, (unsigned char*)leavw, strlen(leavw));
if(ESUCCESS != ret) return ret;
mdelay(100);
ret = WiFiBtDevCmdRet(+OK”);
return ret;
}

31.5.7 获取本地IP

当连接了热点并且使能了DHCP自动获取IP功能,用户可能想要知道本机分配的IP地址是多少,这时候就需要使用指令“AT+LKSTT\r\n”来查询本机IP了,代码如下:

int WiFiBtDevGetLocalIP(void)
{int ret = -EIO;char lkstt[32] = "AT+LKSTT\r\n";ret = pWiFiBtDev->Write(pWiFiBtDev, (unsigned char*)lkstt, strlen(lkstt));if(ESUCCESS != ret) return ret;mdelay(100);unsigned short timeout = 100;while(timeout){unsigned char c = 0;if(pWiFiBtDev->Read(pWiFiBtDev, &c, 1)==1){printf("%c", c);}mdelay(1);timeout--;}return ESUCCESS;
}

由于此指令需要打印”+OK”之后的类容,不容易判定结尾,因而故意设置很长的超时,以便接收到全部数据,再把它打印出来。读者可以改进代码,从中解析出IP。

31.5.8 建立socket连接

W800连上热点之后,要和某个服务器进行网络通信,需要使用指令“AT+SKCT”。这个指令需要传入的参数比较多:协议、本机角色、IP地址、远端端口和本机端口,本书对此指令的参数进行了封装,用一个结构体描述连接信息:

typedef enum{TCP = 0,UDP = 1
}NetworkProtocol;typedef enum{Client = 0,Server = 1
}LocalRole;typedef struct{NetworkProtocol Protocl;LocalRole       Role;char            *IP;unsigned int    RemotePort;unsigned int    LocalPort;unsigned int    SocketPort;
}ConnectInfo;

应用程序要构造一个ConnectInfo结构体,然后构造AT指令,发送给W800。这就是网络连接函数,如果连接成功,还要记录端口号。代码如下:

int WiFiBtDevConnect(ConnectInfo *info)
{int ret = -EIO;char skct[128];sprintf(skct, "AT+SKCT=%d,%d,%s,%d,%d\r\n", \info->Protocl, \info->Role, \info->IP, \info->RemotePort, \info->LocalPort);ret = pWiFiBtDev->Write(pWiFiBtDev, (unsigned char*)skct, strlen(skct));if(ESUCCESS != ret) return ret;mdelay(100);ret = WiFiBtDevCmdRet("+OK=");if(ESUCCESS != ret) return ret;/* 获取连接成功后的socket号 */unsigned short timeout = 1000;unsigned char i = 0;unsigned char buf[32] = {0};while(timeout){if(strstr((char*)buf, "\r\n")){printf("\r\n");break;}else if(pWiFiBtDev->Read(pWiFiBtDev, &buf[i], 1)==1){printf("%c", buf[i]);i++;}mdelay(1);timeout--;}for(i=0;buf[i]!='\r'; i++){if(buf[i]>='0' && buf[i]<='9'){buf[i] = buf[i] - '0';info->SocketPort = info->SocketPort*10 + buf[i];}}printf("IP:%s - SocketPort:%d\r\n", info->IP, info->SocketPort);return ret;
}
  • 第37行:返回的socket号最后一个数字紧跟的是’\r’,在解析socket号的时候不需要这个值,因而for循环的判定就是直到读取到’\r’为止就不进行ASCII到数值的转换计算。

31.5.9 断开指定socket的连接

断开网络连接使用的指令是”AT+SKCLS=&ltsocket&gt\r\n”,本书封装了如下函数:

int WiFiBtDevDisconnect(ConnectInfo info)
{int ret = -EIO;char skcls[32];sprintf(skcls, "AT+SKCLS=%d\r\n", info.SocketPort);ret = pWiFiBtDev->Write(pWiFiBtDev, (unsigned char*)skcls, strlen(skcls));if(ESUCCESS != ret) return ret;mdelay(100);ret = WiFiBtDevCmdRet("+OK");return ret;
}

31.6 测试程序

本实验中,先使用网络调试助手开启了Windows电脑的TCP服务,然后启动板子。板子上的程序先使用W800连接热点,然后后Windows电脑建立TCP连接。

测试函数代码如下所示:

void WiFiBtAppTest(void)
{UartDevicesRegister();TimerDevicesRegister();WiFiBtDevInit();WiFiBtDevSetWorkType(STA);WiFiBtDevEnableDHCP();WiFiBtDevConnectWiFi("X-IOT", "x-iot.cq");WiFiBtDevGetLocalIP();ConnectInfo connect = {.Protocl = TCP,.Role = Client,.IP = "192.168.50.193",.RemotePort = 8080,.LocalPort = 1024};int ret = WiFiBtDevConnect(&connect);while(1){if(ret != ESUCCESS){ret = WiFiBtDevConnect(&connect);delay(1);}}
}

31.7 测试结果

打开串口助手观察信息,打开网络助手启动TCP服务。然后将编译出来的二进制文件烧写到板子上运行,就能在串口助手上观察到W800的响应信息,在网络助手上观察到网络连接信息,如下图所示:


本章完

相关文章:

第31章_瑞萨MCU零基础入门系列教程之WIFI蓝牙模块驱动实验

本教程基于韦东山百问网出的 DShanMCU-RA6M5开发板 进行编写&#xff0c;需要的同学可以在这里获取&#xff1a; https://item.taobao.com/item.htm?id728461040949 配套资料获取&#xff1a;https://renesas-docs.100ask.net 瑞萨MCU零基础入门系列教程汇总&#xff1a; ht…...

arkworks工具栈概览

1. 引言 arkworks定位为zkSNARK编程的Rust生态。其开源代码见&#xff1a; https://github.com/arkworks-rs/ arkworks目前已广泛用于大量项目中&#xff0c;如&#xff1a;Aleo、anoma、celo、Espresso、Findora、Manta、Mina、Nimiq、penumbra等等。 参与arkworks开源实现…...

华为云云服务器云耀L实例评测 | 在华为云耀L实例上搭建电商店铺管理系统:一次场景体验

&#x1f337;&#x1f341; 博主猫头虎&#xff08;&#x1f405;&#x1f43e;&#xff09;带您 Go to New World✨&#x1f341; &#x1f984; 博客首页——&#x1f405;&#x1f43e;猫头虎的博客&#x1f390; &#x1f433; 《面试题大全专栏》 &#x1f995; 文章图文…...

sqlserver存储过程报错:当前事务无法提交,而且无法支持写入日志文件的操作。请回滚该事务。

现象&#xff1a; 系统出现异常&#xff0c;手动执行过程提示如上。 问题排查&#xff1a; 1.直接执行的过程事务挂起&#xff08;排除&#xff09; 2.重启数据库实例&#xff08;重启后无效&#xff09; 3.过程中套用过程&#xff0c;套用的过程中使用事务&#xff0c;因为…...

二刷力扣--字符串

字符串 摘自Python文档-标准库&#xff1a; 在Python中&#xff0c; 字符串是由 Unicode 码位构成的不可变序列。 由于不存在单独的“字符”类型&#xff0c;对字符串做索引操作将产生一个长度为 1 的字符串。 也就是说&#xff0c;对于一个非空字符串 s, s[0] s[0:1]。 不存…...

如何将 OBJ 模型转换和压缩为 GLTF 以与 AWS IoT TwinMaker 配合使用

推荐&#xff1a;使用NSDT场景编辑器快速搭建3D应用场景 概述 在这篇博文中&#xff0c;引用了几种文件扩展名和模型格式。在开始之前&#xff0c;最好了解以下内容&#xff1a; OBJ – 对象文件&#xff0c;一种标准的 3D 图像格式&#xff0c;可以通过各种 3D 图像编辑程序…...

零基础学前端(四)重点讲解 CSS

1. 该篇适用于从零基础学习前端的小白 2. 初学者不懂代码得含义也要坚持模仿逐行敲代码&#xff0c;以身体感悟带动头脑去理解新知识 3. 初学者切忌&#xff0c;不要眼花缭乱&#xff0c;不要四处找其它文档&#xff0c;要坚定一个教授者的方式&#xff0c;将其学通透&#xff…...

类和对象【初始化列表与友元】

全文目录 初始化列表特性 explicit关键字static成员特性 友元友元函数友元类内部类特性 初始化列表 构造函数体中的语句只能将其称为赋初值&#xff0c;而不能称作初始化。因为初始化只能初始化一次&#xff0c;而构造函数体内可以多次赋值。 对象的初始化是在初始化列表进行…...

ActiveRecord::Migration.maintain_test_schema!

测试gem&#xff1a; rspec-rails 问题描述 在使用 rspec-rails 进行测试时&#xff0c;出现了以下错误 ActiveRecord::StatementInvalid: UndefinedFunction: ERROR: function init_id() does not exist这个错误与数据库架构有关。 schema.rb中 create_table "users…...

逆向-beginners之helloworld

#include <stdio.h> int _main() { printf("hello world.\n"); return 0; } // 上面的代码等效于&#xff1a; char *SG3830[] {"hello, world\n"}; int main() { printf("%s", *SG3830); return 0; } #if 0 /* * i…...

如何微调甜甜圈模型——使用示例

Python 中的 Donut 模型可用于从给定图像中提取文本。这在各种场景中都很有用,例如扫描收据。 您可以轻松地。但与人工智能模型一样,您应该根据您的特定需求微调模型。 我编写本教程是因为我没有找到任何资源来准确展示如何使用我的数据集微调 Donut 模型。因此,我必须从其…...

小程序中如何查看指定会员的付款记录

在小程序中&#xff0c;我们可以通过一些简单的步骤来查看指定会员的付款记录。下面是具体的操作流程&#xff1a; 1. 找到指定的会员卡。在管理员后台->会员管理处&#xff0c;找到需要查看付款记录的会员卡。也支持对会员卡按卡号、手机号和等级进行搜索。 2. 查看会员卡…...

LeetCode_贪心算法_困难_630.课程表 III

目录 1.题目2.思路3.代码实现&#xff08;Java&#xff09; 1.题目 这里有 n 门不同的在线课程&#xff0c;按从 1 到 n 编号。给你一个数组 courses &#xff0c;其中 courses[i] [durationi, lastDayi] 表示第 i 门课将会持续上 durationi 天课&#xff0c;并且必须在不晚于…...

Drozer安装

Drozer安装包下载 https://labs.withsecure.com/tools/drozer Drozer需要的python包下载 pip install "pip<21.0" pyOpenSSL pip install "pip<21.0" service_identity pip install "pip<21.0" twisted pip install "pip<…...

752. 打开转盘锁

链接&#xff1a; 752. 打开转盘锁 题解&#xff1a; class Solution { public:int openLock(vector<string>& deadends, string target) {std::unordered_set<std::string> table(deadends.begin(), deadends.end());if (table.find("0000") ! t…...

Bearly:基于人工智能的AI写作文章生成工具

【产品介绍】 名称 Bearly 具体描述 Bearly是一个AI人工智能内容创作工具。你可以用Bearly来阅读、写作、创作&#xff0c;提高你的效率。包括使用Bearly来生成网页的摘要、标题、关键点&#xff0c;也可以用Bearly来生成创意内容、艺术图片、文案编辑等。帮助你克…...

详解哈希,理解及应用

全文目录 概念哈希冲突及原因解决哈希冲突的方法闭散列线性探测二次探测扩容 开散列扩容 哈希的应用位图布隆过滤器 概念 通过映射关系将关键字映射到存储位置&#xff0c;并实现增删改查操作。 通过上面的方法构造出来的结构就叫哈希表&#xff08;散列表&#xff09;&#x…...

解决js加减乘除精度丢失问题

公共类, 将科学计数法的数字转为字符串(以下加减乘除依赖该方法) var toNonExponential (num)> {if(num null) {return num;}if(typeof num "number") {var m num.toExponential().match(/\d(?:\.(\d*))?e([-]\d)/);return num.toFixed(Math.max(0, (m[1] …...

八股——const 关键字

1.const作用 作用&#xff1a;const用于保护指针指向数据不被修改 测试代码1 显示数组的函数不小心修改了指针指向的值&#xff0c;这时候没有加const关键字&#xff0c;编译器不会报错 #include <stdio.h> void showar(int ar[]);int main(void) {int ar[4]{2,3,4,5…...

QT object元对象

qt中的元对象系统提供了对象间通信的信号和槽机制、运行时类型 信息和动态属性系统&#xff1b; 1.该类必须继承自QObject类&#xff1b; 2.必须在类的私有声明区声明Q_OBJECT宏&#xff08;在类定义时&#xff0c;如果没有指定&#xff0c;public或private,则默认为private&a…...

css实现圆环展示百分比,根据值动态展示所占比例

代码如下 <view class""><view class"circle-chart"><view v-if"!!num" class"pie-item" :style"{background: conic-gradient(var(--one-color) 0%,#E9E6F1 ${num}%),}"></view><view v-else …...

椭圆曲线密码学(ECC)

一、ECC算法概述 椭圆曲线密码学&#xff08;Elliptic Curve Cryptography&#xff09;是基于椭圆曲线数学理论的公钥密码系统&#xff0c;由Neal Koblitz和Victor Miller在1985年独立提出。相比RSA&#xff0c;ECC在相同安全强度下密钥更短&#xff08;256位ECC ≈ 3072位RSA…...

【Linux】C语言执行shell指令

在C语言中执行Shell指令 在C语言中&#xff0c;有几种方法可以执行Shell指令&#xff1a; 1. 使用system()函数 这是最简单的方法&#xff0c;包含在stdlib.h头文件中&#xff1a; #include <stdlib.h>int main() {system("ls -l"); // 执行ls -l命令retu…...

iPhone密码忘记了办?iPhoneUnlocker,iPhone解锁工具Aiseesoft iPhone Unlocker 高级注册版​分享

平时用 iPhone 的时候&#xff0c;难免会碰到解锁的麻烦事。比如密码忘了、人脸识别 / 指纹识别突然不灵&#xff0c;或者买了二手 iPhone 却被原来的 iCloud 账号锁住&#xff0c;这时候就需要靠谱的解锁工具来帮忙了。Aiseesoft iPhone Unlocker 就是专门解决这些问题的软件&…...

服务器硬防的应用场景都有哪些?

服务器硬防是指一种通过硬件设备层面的安全措施来防御服务器系统受到网络攻击的方式&#xff0c;避免服务器受到各种恶意攻击和网络威胁&#xff0c;那么&#xff0c;服务器硬防通常都会应用在哪些场景当中呢&#xff1f; 硬防服务器中一般会配备入侵检测系统和预防系统&#x…...

第25节 Node.js 断言测试

Node.js的assert模块主要用于编写程序的单元测试时使用&#xff0c;通过断言可以提早发现和排查出错误。 稳定性: 5 - 锁定 这个模块可用于应用的单元测试&#xff0c;通过 require(assert) 可以使用这个模块。 assert.fail(actual, expected, message, operator) 使用参数…...

SpringBoot+uniapp 的 Champion 俱乐部微信小程序设计与实现,论文初版实现

摘要 本论文旨在设计并实现基于 SpringBoot 和 uniapp 的 Champion 俱乐部微信小程序&#xff0c;以满足俱乐部线上活动推广、会员管理、社交互动等需求。通过 SpringBoot 搭建后端服务&#xff0c;提供稳定高效的数据处理与业务逻辑支持&#xff1b;利用 uniapp 实现跨平台前…...

Spring Boot+Neo4j知识图谱实战:3步搭建智能关系网络!

一、引言 在数据驱动的背景下&#xff0c;知识图谱凭借其高效的信息组织能力&#xff0c;正逐步成为各行业应用的关键技术。本文聚焦 Spring Boot与Neo4j图数据库的技术结合&#xff0c;探讨知识图谱开发的实现细节&#xff0c;帮助读者掌握该技术栈在实际项目中的落地方法。 …...

python执行测试用例,allure报乱码且未成功生成报告

allure执行测试用例时显示乱码&#xff1a;‘allure’ &#xfffd;&#xfffd;&#xfffd;&#xfffd;&#xfffd;ڲ&#xfffd;&#xfffd;&#xfffd;&#xfffd;ⲿ&#xfffd;&#xfffd;&#xfffd;Ҳ&#xfffd;&#xfffd;&#xfffd;ǿ&#xfffd;&am…...

LINUX 69 FTP 客服管理系统 man 5 /etc/vsftpd/vsftpd.conf

FTP 客服管理系统 实现kefu123登录&#xff0c;不允许匿名访问&#xff0c;kefu只能访问/data/kefu目录&#xff0c;不能查看其他目录 创建账号密码 useradd kefu echo 123|passwd -stdin kefu [rootcode caozx26420]# echo 123|passwd --stdin kefu 更改用户 kefu 的密码…...