STM32标准库——(17)硬件SPI读写W25Q64

1.SPI外设简介

时钟频率就是sck波形的频率,一个sck时钟交换一个bit,所以时钟频率一般体现的是传输速度,单位是Hz或者bit/s,那这里的时钟频率是fPCLK除以一个分频系数分频系数可以配置为2或4或8、16、32、64、128、256,所以可以看出来,spi的时钟其实就是由pclk分频得来的,pclk就是外设时钟,APB2的pclk就是72MHz,APB1的pclk是36MHz,比如我们的spi1是APB2的外设,pclk等于72MHz,那它的spi时钟频率最大就是只进行二分频=36MHz,像我们之前I2C的频率最大就只有400KHz,所以这里spi的最大频率比I2C快了90倍,然后这里频率有些注意事项,一是这个频率数值并不是任意指定的,它只能是pclk执行分频后的数值就只有这八个选项,最低频率是pclk的256分频,二是spi1和spi2 挂载的总线是不一样的,spi1挂载在APB2,pclk是72MHz,spi1挂载在APB1,pclk是36MHz,所以同样的配置,spi1的时钟频率要比spi2的大一倍。

2.SPI框图

  1. 首先左上角核心部分就这个移位寄存器,右边的数据低位,一位一位的从mosi移出去,然后miso的数据一位一位的移入到左边的数据高位,显然移位寄存器应该是一个右移的状态,所以目前图上表示的是低位先行的配置,对应右下角有一个LSBFIRST的控制位,这一位可以控制是低位先行还是高位先行,手册里寄存器描述可以查一下,这里LSBFIRST帧格式,给0先发送msb,msb就是高位的意思,给1先发送lsb ,lsb就是低位的意思,那ppt这里目前的状态LSBFIRST的应该是1,低位先行,如果LSBFIRST给零高位先行的话,这个图还要变动一下,就是移位寄存器变为左移,输出,从左边移出去,输入,从右边移进来,这样才符合逻辑,然后继续看左边这一块,这里画了个方框,里面把mosi和miso做了个交叉,这一块主要是用来进行主从模式引脚变换的,我们这个spi外设可以做主机,也可以做从机,做主机时这个交叉就不用,mosi为mo,主机输出,miso为mi,主机输入,这是主机的情况,如果我们stm32作为从机的话,mosi为si,从机输入,这时他就要走交叉的这一路,输入到移位寄存器,同理miso为so,从机输出,这时输出的数据也走交叉的这一路输出到miso。
  2. 接下来上下两个缓冲区,就还是我们熟悉的设计,这两个缓冲区实际上就是数据寄存器DR,下面发送缓冲区就是发送数据寄存器TDR,上面接收缓冲区就是接收数据寄存器RDR,和串口那里一样,TDR和RDR占用同一个地址统一叫做DR,写入DR时数据从这里写入到TDR,读取DR时,数据从这里从RDR读出,数据寄存器和移位寄存器打配合,可以实现连续的数据流,具体流程就是比如我们需要连续发送一批数据,第一个数据写入到TDR当移位寄存器没有数据移位时TDR的数据会立刻转入移位寄存器开始移位,这个转入时刻,会置状态寄存器的TXE为1表示发送寄存器空当我们检查TXE置1后,紧跟着下一个数据就可以提前写入到TDR里侯着了,一旦上个数据发完,下一个数据就可以立刻跟进,实现不间断的连续传输,然后移位寄存器,这里一旦有数据过来了,它就会自动产生时钟将数据移出去,在移出的过程中,miso的数据也会移入,一旦数据移出完成,数据移入是不是也完成了,这时移入的数据就会整体的从移位计算器转入到接收缓冲区RDR,这个时刻会置状态寄存器器的RXNE为1,表示接收计寄存器器非空当我们检查RXNE置1后,就要尽快把数据从RDR读出来,在下一个数据到来之前,读出RDR就可以实现连续接收,否则如果下一个数据已经收到了,上个数据还没从RDR读出来,那RDR的数据就会被覆盖,就不能实现连续的数据流了。和之前串口、I2C的都差不多的,当然这三者也是有一些区别的,比如这里spi全双工发送和接收同步进行,所以它的数据寄存器发送和接收是分离的,而移位寄存器发送和接收可以共用;然后看一下前面I2C的框图,因为I2C是半双工发送和接收不会同时进行,所以它的数据寄存器和移位寄存器,发送和接收都可以是共用的;串口是全双工,并且发送和接收可以异步进行,所以这就要求它的数据寄存器,发送和接收是分离的,移位寄存器发送和接收也得是分离的。
  3. 接着后面这些通信电路和各种寄存器,都是一些黑盒子电路,如果你要具体研究,可以看一下这些位的寄存器描述,我挑几个重点的讲一下,比如LSBFIRST的刚才说过,决定高位先行还是低位先行spe是spi使能,就是SPI_Cmd函数配置的位BR配置波特率,就是sck时钟频率MSTR(Master),配置主从模式,1是主模式,0是从模式,我们一般用主模式cpul和cpha,这个之前讲过,用来选择spi的四种模式,然后这里sr状态计算器,最后两个txe发送寄存器空,rxne接收寄存器非空,这两个比较重要,我们发送接收数据的时候需要关注这两位,最后CR2寄存器就是一些使能位了,比如中断使能dma使能等。
  4. 最后这里还有一个NSS引脚,ss就是从机选择,低电平有效,所以这里前面加了个n,这个nss和我们想象的从机选择可能不太一样,我们想象的应该是用来指定某个从机对吧,但是根据手册里的描述,我也研究了一下,这里的nss设计,可能更偏向于实现这里说的多主机模型,总的来说啊,这个NSS我们并不会用到,SS引脚我们直接使用一个gpio模拟就行,因为ss引脚很简单,就置一个高低电平就行了,而且从机的情况下,ss还会有多个,这里硬件的nss也完成不了我们想要的功能,那这个nss是如何实现多主机切换的功能呢,假如这里有三个stm32设备,我们需要把这三个设备的nss全都连接在一起,首先这个nss可以配置为输出或者输入,当配置为输出时,可以输出电平告诉别的设备,我现在要变为主机,你们其他设备都给我变从机,不要过来捣乱,当配置为输入时,可以接收别设备的信号当有设备是主机拉低nss后,我就无论如何也变不成主机了,这就是它的作用,然后内部电路的设计,当这里这个ssoe等于1时,nss作为输出引脚,并在当前设备变为主设备时,给nss输出低电平,这个输出的低电平,就是告诉其他设备,我现在是主机了,当主机结束后,ssoe要清0,nss变为输入,这时输入信号就会跑到右边这里,这个数据选择器ssm位决定选择哪一路,当选择上面一路时是硬件nss模式,也就是说这时外部如果输入了低电平,那当前的设备就进入不了主模式了,因为nss低电平肯定是,外部已经有设备进入了主模式,他已经提前告诉我他是主模式了,我就不能再跟大家抢了,当数据选择器选择下面一路时,是软件管理nss输入,nss是1还是2,由这一位SSI来决定,这个就是nss实现多主机的思路,但这个设计是nss作为多从机选择的作用消失了,揪出所有人的小辫子之后,主机发送的数据就只能是广播发送给所有人的,如果想实现指定设备通信,可能还需要再加入寻址机制。

3.SPI基本结构

4.主模式传输

4.1 连续传输

  • 第一行是sck时钟线,这里cpol等于1,cpha等于1,示例使用的是spi模式三,所以sck默认是高电平,然后在第一个下降沿mosi和miso移出数据,之后上升沿引入数据,依次这样来进行,那下面第二行是mosi和miso输出的波形,跟随sck时钟变化,数据位依次出现,这里从前到后依次出现的是b0b1一直到b7 ,所以这里示例演示的是低位先行的模式啊,实际spi高位先行用的多一些,最后第三行是txe发送寄存器空标志位,下面发送缓冲器括号写入SPI_DR,实际上就是这里的TDR然后bsy(busy)是由硬件自动设置和清除的当有数据传输时,busy置1那上面红框这部分演示的就是输出的流程和现象,然后下面红框是输入的流程和现象,第一个是miso/mosi的输入数据,之后是RXNE接收数据寄存器非空标志位最后是接收,缓冲器读出SPI_DR,显然这里就是RDR

  • 首先ss置低电平开始时序,这个没画但是必须得有的,在刚开始时TXE为1,表示TDR空可以写入数据开始传输,然后下面指示的第一步就是软件写入0xf1至SPI_DR,0xf1就是要发送的第一个数据,之后可以看到写入之后TDR变为0xf1 ,同时txe变为0,表示tdr已经有数据了,那此时dr是等候区,移位寄存器才是真正的发送区,移位寄存器刚开始肯定没有数据,所以在等候区TDR里的f1 ,就会立刻转入移位寄存器开始发送转入瞬间置txe标志位为1,表示发送寄存器空,然后移位寄存器有数据了,波形就自动开始生成,当然我感觉这里画的数据波形时机可能有点早,应该是在这个时刻b0的波形才开始产生,在这之前数据还没有转入移位进器,所以感觉b0出现的可能过早了,不过这个也不影响我们理解,大家知道这意思就行好了,这样数据转入移位寄存器之后,数据F1的波形就开始产生了,在移位产生f1波形的同时,等候区tdr是空的,为了移位完成时,下一个数据能不间断的跟随,这里我们就要提早把下一个数据写入到TDR里等着了,所以下面只是第二步的操作,是写入F1之后,软件等待TXE等于1,在这个位置,一旦tdr空了,我们就写入F2至SPI_DR,写入之后可以看到tdr的内容就变成F2了,也就是把下一个数据放到tdr里,后者之后的发送流程也是同理,最后在这里如果我们只想发送三个数据,F3转入移位寄存器之后,TXE等于1,我们就不需要继续写入了,txe之后一直是1,注意在最后一个TXE等于1之后,还需要继续等待一段时间,f3的波形才能完整发送完,等波形全部完整发送之后,busy的标志由硬件清除,这才表示波形发送完成了,那这些就是发送的流程,然后继续看一下下面接收的流程,SPI是全双工,发送的同时还有接收,所以可以看到在第一个字节发送完成后,第一个字节的接收也完成了,接收到的数据1是A1 ,这时移位寄存器的数据整体转入RDR,RDR随后存储的就是A1 ,转入的同时按RXNE标志位也置1,表示收到数据了,我们的操作是下面这里写的,软件等待RXNE等于1,=1表示收到数据了,然后从SPI_DR也是RDR读出数据A1 ,这是第一个接收到的数据,接收之后软件清除RXNE标志位,然后当下一个数据2收到之后,RXNE重新置1,我们监测到RXNE等于1时就继续读出RDR,这是第二个数据A2 ,最后在最后一个字节时序完全产生之后,数据三才能收到,所以数据3,直到这里才能读出来,然后注意,一个字节波形收到后,移位寄存器的数据自动转入RDR,会覆盖原有的数据,所以我们读出rdr要及时,比如A1这个数据收到之后,最迟你也要在这里把它读走,否则下一个数据A2覆盖A1,就不能实现连续数据流的接收了。

4.2 非连续传输

配置还是spi模式三,sck默认高电平,我们想发送数据时如果检测到TXE等于1了,TDR为空,就软件写入0xF1至SPI_DR,这时TDR的值变为F1,TXE变为0,目前移位寄存器也是空,所以这个F1会立刻转入移位寄存器,开始发送,波形产生并且,TXE置回1,表示你可以把下一个数据放在tdr里侯着了,但是现在区别就来了,在连续传输这里一旦,TXE等于1了,我们就会把下个数据写到tdr里侯着这样是为了连续传输数据衔接更紧密,但是刚才说了,这样的话,流程就比较混乱,程序写起来比较复杂,所以在非连续传输这里,TXE等于1了,我们不着急把下一个数据写进去,而是一直等待,等第一个字节时序结束,在这个位置时序结束了,意味着接收第一个字节也完成了,这时接收的RXNE会置一,我们等待RXNE置1后,先把第一个接收到的数据读出来,之后再写入下一个字节数据,也就是这里的软件等待TXE等于1,但是较晚写入0xf2SPI_DR,较晚写入TDR后,数据二开始发送,我们还是不着急写数据三,等到了这里,先把接收的数据二收着,再继续写入数据3,数据3时序结束后,最后再接收数据三置换回来的数据,你看按照这个流程的话,我们的整个步骤就是第一步等待TXE为一,第二步写入发送的数据至TDR,第三步等待RXNE为一,第四步读取RDR接收的数据,之后交换第二个字节,重复这四步,那这样我们就可以把这四部分装到一个函数,调用一次交换一个字节,这样程序逻辑是不是就非常简单了,和之前软件spi的流程基本上是一样的,我们只需要稍作修改,就可以把软件spi改成硬件spi,那非连续算出缺点,就是在这个位置没有及时把下一个数据写入TDR侯着,所以等到第一个字节时序完成后,第二个字节还没有送过来,那这个数据传输就会在这里等着,所以这里时钟和数据的时序,在字节与字节之间会产生间隙,拖慢了整体数据传输的速度这个间隙在sck频率低的时候影响不大,但是在sck频率非常高时隙拖后腿的现象就比较严重了。

5.软/硬件波形对比

6.常用API

6.1 SPI_I2S_SendData

/*** @brief  Transmits a Data through the SPIx/I2Sx peripheral.* @param  SPIx: where x can be*   - 1, 2 or 3 in SPI mode *   - 2 or 3 in I2S mode* @param  Data : Data to be transmitted.* @retval None*/
void SPI_I2S_SendData(SPI_TypeDef* SPIx, uint16_t Data)
功能:通过外设 SPIx 发送一个数据
参数:SPIx:x 可以是 1 或者 2,来选择 SPI 外设Data: 待发送的数据
返回值:无       

6.2 SPI_I2S_ReceiveData

/*** @brief  Returns the most recent received data by the SPIx/I2Sx peripheral. * @param  SPIx: where x can be*   - 1, 2 or 3 in SPI mode *   - 2 or 3 in I2S mode* @retval The value of the received data.*/
uint16_t SPI_I2S_ReceiveData(SPI_TypeDef* SPIx)
功能:返回通过 SPIx 最近接收的数据
参数:SPIx:x 可以是 1 或者 2,来选择 SPI 外设
返回值:接收到的字       

6.3 SPI_I2S_GetFlagStatus

/*** @brief  Checks whether the specified SPI/I2S flag is set or not.* @param  SPIx: where x can be*   - 1, 2 or 3 in SPI mode *   - 2 or 3 in I2S mode* @param  SPI_I2S_FLAG: specifies the SPI/I2S flag to check. *   This parameter can be one of the following values:*     @arg SPI_I2S_FLAG_TXE: Transmit buffer empty flag.*     @arg SPI_I2S_FLAG_RXNE: Receive buffer not empty flag.*     @arg SPI_I2S_FLAG_BSY: Busy flag.*     @arg SPI_I2S_FLAG_OVR: Overrun flag.*     @arg SPI_FLAG_MODF: Mode Fault flag.*     @arg SPI_FLAG_CRCERR: CRC Error flag.*     @arg I2S_FLAG_UDR: Underrun Error flag.*     @arg I2S_FLAG_CHSIDE: Channel Side flag.* @retval The new state of SPI_I2S_FLAG (SET or RESET).*/
FlagStatus SPI_I2S_GetFlagStatus(SPI_TypeDef* SPIx, uint16_t SPI_I2S_FLAG)
功能:检查指定的 SPI 标志位设置与否
参数:SPIx:x 可以是 1 或者 2,来选择 SPI 外设SPI_I2S_FLAG:待检查的 SPI 标志位
返回值:SPI_FLAG 的新状态(SET 或者 RESET)  

6.4 SPI_I2S_ClearFlag

/*** @brief  Clears the SPIx CRC Error (CRCERR) flag.* @param  SPIx: where x can be*   - 1, 2 or 3 in SPI mode * @param  SPI_I2S_FLAG: specifies the SPI flag to clear. *   This function clears only CRCERR flag.* @note*   - OVR (OverRun error) flag is cleared by software sequence: a read *     operation to SPI_DR register (SPI_I2S_ReceiveData()) followed by a read *     operation to SPI_SR register (SPI_I2S_GetFlagStatus()).*   - UDR (UnderRun error) flag is cleared by a read operation to *     SPI_SR register (SPI_I2S_GetFlagStatus()).*   - MODF (Mode Fault) flag is cleared by software sequence: a read/write *     operation to SPI_SR register (SPI_I2S_GetFlagStatus()) followed by a *     write operation to SPI_CR1 register (SPI_Cmd() to enable the SPI).* @retval None*/
void SPI_I2S_ClearFlag(SPI_TypeDef* SPIx, uint16_t SPI_I2S_FLAG)
功能:清除 SPIx 的待处理标志位
参数:SPIx:x 可以是 1 或者 2,来选择 SPI 外设SPI_I2S_FLAG:待清除的 SPI 标志位
返回值:无

7.接线图

 

8.相关代码

8.1 MySPI.c

#include "stm32f10x.h"                  // Device header/*** 函    数:SPI写SS引脚电平,SS仍由软件模拟* 参    数:BitValue 协议层传入的当前需要写入SS的电平,范围0~1* 返 回 值:无* 注意事项:此函数需要用户实现内容,当BitValue为0时,需要置SS为低电平,当BitValue为1时,需要置SS为高电平*/void MySPI_W_SS(uint8_t BitValue)
{GPIO_WriteBit(GPIOA, GPIO_Pin_4, (BitAction)BitValue);		//根据BitValue,设置SS引脚的电平
}/*** 函    数:SPI初始化* 参    数:无* 返 回 值:无* 注意事项:此函数需要用户实现内容,实现SS、SCK、MOSI和MISO引脚的初始化*/
void MySPI_Init(void)
{/*开启时钟*/RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);	//开启GPIOA的时钟RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);	//开启SPI1的时钟/*GPIO初始化*/GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIO_InitStructure);					//将PA4引脚初始化为推挽输出 SS是软件控制的输出信号 配置通用推挽输出GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_7;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIO_InitStructure);					//将PA5和PA7引脚初始化为复用推挽输出 MOSI和SCK是外设输出的控制GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIO_InitStructure);					//将PA6引脚初始化为上拉输入 SPI_InitTypeDef SPI_InitStructure;SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_128;		//波特率分频,选择128分频SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge;			//SPI相位,选择第一个时钟边沿采样,极性和相位决定选择SPI模式0 1Edge代表CPHA=0 2Edge代表CPHA=1SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low;				//SPI极性,选择低极性 这里46和45配合使用模式0 都需要配置低电平SPI_InitStructure.SPI_CRCPolynomial = 7;				//CRC多项式,暂时用不到,给默认值7SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;		//数据宽度,选择为8位SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;		//先行位,选择高位先行 49和48行常用8位高位先行 在SPI外设简介提到SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;		//方向,选择2线全双工SPI_InitStructure.SPI_Mode = SPI_Mode_Master;			//模式,选择为SPI主模式SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;				//NSS,选择由软件控制SPI_Init(SPI1,&SPI_InitStructure);SPI_Cmd(SPI1,ENABLE);MySPI_W_SS(1);
}/*** 函    数:SPI起始* 参    数:无* 返 回 值:无*/
void MySPI_Start(void)
{MySPI_W_SS(0);				//拉低SS,开始时序
}/*** 函    数:SPI终止* 参    数:无* 返 回 值:无*/
void MySPI_Stop(void)
{MySPI_W_SS(1);				//拉高SS,终止时序
}/*** 函    数:SPI交换传输一个字节,使用SPI模式0* 参    数:ByteSend 要发送的一个字节* 返 回 值:接收的一个字节*/
uint8_t MySPI_SwapByte(uint8_t ByteSend)
{while(SPI_I2S_GetFlagStatus(SPI1,SPI_I2S_FLAG_TXE) != SET);		//等待发送数据寄存器空SPI_I2S_SendData(SPI1,ByteSend);								//写入数据到发送数据寄存器,开始产生时序while(SPI_I2S_GetFlagStatus(SPI1,SPI_I2S_FLAG_RXNE) != SET);	//等待接收数据寄存器非空return SPI_I2S_ReceiveData(SPI1);								//读取接收到的数据并返回
}

8.2 MySPI.h

#ifndef __MYSPI_H
#define __MYSPI_Hvoid MySPI_Init(void);
void MySPI_Start(void);
void MySPI_Stop(void);
uint8_t MySPI_SwapByte(uint8_t ByteSend);#endif

8.3 main.c

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "W25Q64.h"uint8_t MID;							//定义用于存放MID号的变量
uint16_t DID;							//定义用于存放DID号的变量uint8_t ArrayWrite[] = {0x01, 0x02, 0x03, 0x04};	//定义要写入数据的测试数组
uint8_t ArrayRead[4];								//定义要读取数据的测试数组int main(void)
{/*模块初始化*/OLED_Init();						//OLED初始化W25Q64_Init();						//W25Q64初始化/*显示静态字符串*/OLED_ShowString(1, 1, "MID:   DID:");OLED_ShowString(2, 1, "W:");OLED_ShowString(3, 1, "R:");	/*显示ID号*/W25Q64_ReadID(&MID, &DID);			//获取W25Q64的ID号OLED_ShowHexNum(1, 5, MID, 2);		//显示MIDOLED_ShowHexNum(1, 12, DID, 4);		//显示DID/*W25Q64功能函数测试*/W25Q64_SectorErase(0x000000);					//扇区擦除W25Q64_PageProgram(0x000000, ArrayWrite, 4);	//将写入数据的测试数组写入到W25Q64中W25Q64_ReadData(0x000000, ArrayRead, 4);		//读取刚写入的测试数据到读取数据的测试数组中/*显示数据*/OLED_ShowHexNum(2, 3, ArrayWrite[0], 2);		//显示写入数据的测试数组OLED_ShowHexNum(2, 6, ArrayWrite[1], 2);OLED_ShowHexNum(2, 9, ArrayWrite[2], 2);OLED_ShowHexNum(2, 12, ArrayWrite[3], 2);OLED_ShowHexNum(3, 3, ArrayRead[0], 2);			//显示读取数据的测试数组OLED_ShowHexNum(3, 6, ArrayRead[1], 2);OLED_ShowHexNum(3, 9, ArrayRead[2], 2);OLED_ShowHexNum(3, 12, ArrayRead[3], 2);while (1){}
}

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

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

相关文章

输出X^N对233333取模的结果。

对任意正整数N&#xff0c;求XN%233333的值。 要求运算的时间复杂度为O(logN)。 例如X30 X15*X15X15X7*X7*XX7X3*X3*XX3X*X*X共7次乘法运算完毕。输入输出格式 输入描述: 输入两个整数X和N&#xff0c;用空格隔开&#xff0c;其中X,N<10^9。 输出描述: 输出X^N对233333取模…

STL容器之map和set的补充AVL树

一、AVL树 ​ 不同搜索的对比&#xff1a;1.暴力搜索时间复杂度是O(N)&#xff1b;2.二分查找的时间复杂度是O(lgN)&#xff0c;但是伴随着有序&#xff0c;插入删除挪动数据的成本极高&#xff1b;3.二叉搜索的时间复杂度是高度次数&#xff0c;极端场景会退化为类似链表时间…

【送书活动1】基于React低代码平台开发:构建高效、灵活的应用新范式

【送书活动1】基于React低代码平台开发&#xff1a;构建高效、灵活的应用新范式 写在最前面一、React与低代码平台的结合优势二、基于React的低代码平台开发挑战三、基于React的低代码平台开发实践四、未来展望《低代码平台开发实践&#xff1a;基于React》编辑推荐内容简介作者…

蓝桥杯知识点对应的复习题

【注】 long long 字母大写变小写32 分离整数的各个位数 int main() {int a; cin >> a;while (a>0){cout << a % 10 << " ";a / 10;}return 0; } 素数 试除法 bool check(int n) {if (n < 2)return false;for (int i 2; i < sqrt(n)…

cookie和session的区别(七大不同)

文章目录 cookie和session的区别&#xff08;七大不同&#xff09;1、存储位置不同:2、安全性:3、性能方面:4、存储大小:5、生命周期:6、使用场景:7、数据类型不同&#xff1a; cookie和session的区别&#xff08;七大不同&#xff09; 1、存储位置不同: Cookie 存储在客户端…

基于springboot实现的学生评奖评优管理系统的设计与实现(源码|论文)

系统概述 springboot实现的学生评奖评优管理系统的设计与实现&#xff0c;系统分Web端和后台管理端&#xff0c;后端采用的是springbootmybatis&#xff0c;前端采用vuehtml&#xff0c;代码简洁易懂&#xff0c;有配套论文初稿&#xff0c;适合新手小白学变编程... 一、系统架…

RocketMQ—RocketMQ集成SpringBoot

RocketMQ—RocketMQ集成SpringBoot 新建生产者的boot项目和消费者的boot项目&#xff0c;pom文件重点如下&#xff1a; <dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</arti…

Docker 中的挂载机制

Docker 挂载机制 Docker 的挂载机制允许将宿主机的文件或目录挂载到 Docker 容器内部&#xff0c;这样容器就可以访问宿主机上的文件系统。Docker 提供了多种挂载方式&#xff0c;包括以下几种&#xff1a; 绑定挂载&#xff08;Bind Mounts&#xff09;&#xff1a;通过绑定挂…

Docker中使用nginx-rtmp推拉网络摄像头视频流

前言&#xff1a; 该部分比较麻烦&#xff0c;闹腾了好久&#xff08;ffmpeg推拉流没学过&#xff0c;事实证明依葫芦画瓢是不行滴&#xff0c;后面有时间再学吧&#xff09;&#xff0c;后来借助chatGPT勉强解决&#xff0c;但不是很懂。因个人能力有限&#xff0c;只复述操作…

Starknet(strk) 跨链桥教程:手把手教你用bitget钱包跨链

摘要&#xff1a;通过Rhino.fi &#xff0c;将资产无缝桥接至Starknet&#xff08;web3.bitget.com/zh/assets/starknet-wallet&#xff09;变得高效且具有成本效益&#xff0c;Rhino.fi 是一个以其快速处理时间和低交易费用而闻名的平台。它专为与 MetaMask 等流行的 Web 3 钱…

长整数拼接后求余数

原题&#xff1a;2024牛客寒假算法基础集训营3 M题 方法一&#xff1a; 用递归进行长整数除法&#xff0c;暴力寻找&#xff0c;但是会超时 #include <bits/stdc.h> using namespace std; bool f(string &s, int ans,int count) {if (count > s.size()) {if (a…

Newsmy储能电源与您相约九州汽车生态博览

2024年3月7日—10日&#xff0c;第24届 深圳国际智慧出行、汽车改装及汽车服务业态博览会&#xff08;以下简称“九州汽车生态博览会”&#xff09;将在深圳国际会展中心&#xff08;宝安&#xff09;举办&#xff0c;Newsmy纽曼集团将在3号馆32523展位&#xff0c;携全系产品与…

2024年3月6日 十二生肖 今日运势

小运播报&#xff1a;2024年3月6日&#xff0c;星期三&#xff0c;农历正月廿六 &#xff08;甲辰年丁卯月己巳日&#xff09;&#xff0c;法定工作日。 红榜生肖&#xff1a;牛、猴、鸡 需要注意&#xff1a;鼠、虎、猪 喜神方位&#xff1a;东北方 财神方位&#xff1a;正…

nginx 常见面面问题

1.什么是 Nginx&#xff1f; Nginx 是一个 轻量级 / 高性能的反向代理 Web 服务器&#xff0c;用于 HTTP、HTTPS、SMTP、POP3 和 IMAP 协议。他实现非常高效的反向代理、负载平衡&#xff0c;他可以处理 2-3 万并发连接数&#xff0c;官方监测能支持 5 万并发&#xff0c;现在中…

Java毕业设计 基于SpringBoot 众筹网

Java毕业设计 基于SpringBoot 众筹网 SpringBoot 众筹网 功能介绍 注册 邮箱验证码 登录 忘记密码 首页 图片轮播 关于我们 项目列表 发布项目 我的添加项目 提交审核 已在募捐 项目详情 项目介绍 项目进展 捐赠列表 评论 新闻列表 发布新闻 新闻详情 评论新闻 联系我们 提交…

Linux笔记--静态库和动态库

库是指在我们的应用中&#xff0c;有一些公共代码是需要反复使用&#xff0c;就把这些代码编译为"库"文件;在链接步骤中&#xff0c;链接器将从库文件取得所需的代码&#xff0c;复制到生成的可执行文件中。 Linux中常见的库文件有两种&#xff0c;一种.a为后缀&…

GIS之深度学习10:运行Faster RCNN算法

&#xff08;未完成&#xff0c;待补充&#xff09; 获取Faster RCNN源码&#xff08;开源的很多&#xff09; 替换自己的数据集&#xff08;图片标签文件&#xff09; 打开终端&#xff0c;进入gpupytorch环境 运行voc_annotation.py文件生成与训练文件 E:\DeepLearningMode…

Linux配置VNC实现远程控制,提高运维效率

VNC介绍 1.1 VNC简介 VNC (Virtual Network Console)是虚拟网络控制台的缩写。它 是一款优秀的远程控制工具软件&#xff0c;可以实现远程控制计算机的功能。 1.2 VNC组成 VNC基本上是由两部分组成&#xff0c;在任何安装了客户端的应用程序(vncviewer)的计算机都能十分方便…

蓝桥杯第1390题——A Careful Approach

题目描述 如果你认为参加一个编程比赛让你感到有压力&#xff0c;那么请你想象你是一个空中交通管制员。因为人命关天&#xff0c;所以一个空中交通管制员必须在时刻变化的环境中专注于任务&#xff0c;解决不可预知的事件。 让我们将目光转向飞机的着陆流程。飞机进入目的地飞…

人事档案转出需要注意哪些方面

人事档案转出是指将员工的人事档案从一个部门、公司或组织转移到另一个部门、公司或组织的过程。这个过程需要注意以下几个方面&#xff1a; 1.法律合规&#xff1a;在进行人事档案转出前&#xff0c;要确保遵守相关的法律法规和公司内部规定。例如&#xff0c;要确保有合法的授…