当前位置: 首页 > 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…...

脑机新手指南(八):OpenBCI_GUI:从环境搭建到数据可视化(下)

一、数据处理与分析实战 &#xff08;一&#xff09;实时滤波与参数调整 基础滤波操作 60Hz 工频滤波&#xff1a;勾选界面右侧 “60Hz” 复选框&#xff0c;可有效抑制电网干扰&#xff08;适用于北美地区&#xff0c;欧洲用户可调整为 50Hz&#xff09;。 平滑处理&…...

JavaScript 中的 ES|QL:利用 Apache Arrow 工具

作者&#xff1a;来自 Elastic Jeffrey Rengifo 学习如何将 ES|QL 与 JavaScript 的 Apache Arrow 客户端工具一起使用。 想获得 Elastic 认证吗&#xff1f;了解下一期 Elasticsearch Engineer 培训的时间吧&#xff01; Elasticsearch 拥有众多新功能&#xff0c;助你为自己…...

【Java学习笔记】Arrays类

Arrays 类 1. 导入包&#xff1a;import java.util.Arrays 2. 常用方法一览表 方法描述Arrays.toString()返回数组的字符串形式Arrays.sort()排序&#xff08;自然排序和定制排序&#xff09;Arrays.binarySearch()通过二分搜索法进行查找&#xff08;前提&#xff1a;数组是…...

SCAU期末笔记 - 数据分析与数据挖掘题库解析

这门怎么题库答案不全啊日 来简单学一下子来 一、选择题&#xff08;可多选&#xff09; 将原始数据进行集成、变换、维度规约、数值规约是在以下哪个步骤的任务?(C) A. 频繁模式挖掘 B.分类和预测 C.数据预处理 D.数据流挖掘 A. 频繁模式挖掘&#xff1a;专注于发现数据中…...

pam_env.so模块配置解析

在PAM&#xff08;Pluggable Authentication Modules&#xff09;配置中&#xff0c; /etc/pam.d/su 文件相关配置含义如下&#xff1a; 配置解析 auth required pam_env.so1. 字段分解 字段值说明模块类型auth认证类模块&#xff0c;负责验证用户身份&am…...

Python如何给视频添加音频和字幕

在Python中&#xff0c;给视频添加音频和字幕可以使用电影文件处理库MoviePy和字幕处理库Subtitles。下面将详细介绍如何使用这些库来实现视频的音频和字幕添加&#xff0c;包括必要的代码示例和详细解释。 环境准备 在开始之前&#xff0c;需要安装以下Python库&#xff1a;…...

Springboot社区养老保险系统小程序

一、前言 随着我国经济迅速发展&#xff0c;人们对手机的需求越来越大&#xff0c;各种手机软件也都在被广泛应用&#xff0c;但是对于手机进行数据信息管理&#xff0c;对于手机的各种软件也是备受用户的喜爱&#xff0c;社区养老保险系统小程序被用户普遍使用&#xff0c;为方…...

【7色560页】职场可视化逻辑图高级数据分析PPT模版

7种色调职场工作汇报PPT&#xff0c;橙蓝、黑红、红蓝、蓝橙灰、浅蓝、浅绿、深蓝七种色调模版 【7色560页】职场可视化逻辑图高级数据分析PPT模版&#xff1a;职场可视化逻辑图分析PPT模版https://pan.quark.cn/s/78aeabbd92d1...

【Android】Android 开发 ADB 常用指令

查看当前连接的设备 adb devices 连接设备 adb connect 设备IP 断开已连接的设备 adb disconnect 设备IP 安装应用 adb install 安装包的路径 卸载应用 adb uninstall 应用包名 查看已安装的应用包名 adb shell pm list packages 查看已安装的第三方应用包名 adb shell pm list…...

STM32---外部32.768K晶振(LSE)无法起振问题

晶振是否起振主要就检查两个1、晶振与MCU是否兼容&#xff1b;2、晶振的负载电容是否匹配 目录 一、判断晶振与MCU是否兼容 二、判断负载电容是否匹配 1. 晶振负载电容&#xff08;CL&#xff09;与匹配电容&#xff08;CL1、CL2&#xff09;的关系 2. 如何选择 CL1 和 CL…...