STM32之SPI总线

一、SPI总线概述

1、SPI总线介绍

SPI是一种通信协议,它是摩托罗拉公司研发出来的一种通信协议,就有自己的特点(串行,并行,单工,半双工,全双工,同步异步)。它主要应用于音视频的开发. SPI是串行外设接口(Serial Peripheral Interface)的缩写,是一种高速的,全双工,同步的通信总线,并且在芯片的管脚上只占用四根线,节约了芯片的管脚,同时为PCB的布局上节省空间,提供方便,正是出于这种简单易用的特性,越来越多的芯片集成了这种通信协议.

一般的通信速度可以达到几十Mhz

SPI(Serial Peripheral Interface)是一种串行外设接口标准,用于在微控制器或其他数字设备之间进行通信。它被广泛应用于各种应用领域,如通信、嵌入式系统和传感器网络等。

 2、SPI总线接口与物理拓扑结构

SPI按照接线的不同分为3线SPI与4线SPI。

同步串行全双工

同步串行半双工

物理上的连接是怎么样的?

使用SPI协议重点是知道里面的一个时序的情况,如果知道了这种情况,完全可以实现这种协议。

SPI协议是一种一主多从的形式:

可以通过片选线来选中不同的从机

时钟线(信号)只能由主机控制,从机受到主机发出的时钟信号来做出相应的处理

针对主机而言:

当时钟线产生上升沿时接收数据,当时钟线产生下降沿时发送数据

当时钟线产生下降沿时接收数据,当时钟线产生上升沿时发送数据

3、SPI总线通信原理

SPI可以配置为4种模式(SPI控制器)

MODE0-MODE3

4、SPI总线数据格式

8位的数据,高位先发

SPI的通信过程其实就是数据交换的过程

首先需要拉低片选线

再根据当前的边沿决定是发送数据还是接收数据(上升沿发送,下降沿接收)

结束本次通信拉高片选线

用IO口来实现SPI协议?

写伪代码(不是真正可以实现的,不过可以实现相应的逻辑)

当前选择模式0:CPHA = 0, CPOL = 0,下降沿发送数据,上升沿接收数据

SCK, MOSI, MISO,CS

U8 Data = 0;//0000 0000
CS = 0;//选中这个芯片
SCK = 1;
for(i =0; i <8 ;i++)
{
    SCK = 0;
    MOSI = 0/1;
    SCK = 1;
    Data <<= 1;//空出低位的操作,data = 0000 0100
    if(MISO)
    {
        Data |= 1;//0000 0001
    }
}

二、SPI控制器

1、SPI的控制器特征

基于三条线的全双工同步传输(通过一个标志位的切换达到所谓的双工通信)
   
基于双线的单工同步传输,其中一条可作为双向数据线
   
8 位或 16 位传输帧格式选择
    主模式或从模式操作
   
多主模式功能
   
8 个主模式波特率预分频器(最大值为 fPCLK/2)fpclk就是看当前用的SPI是在哪根总线上
   
从模式频率(最大值为 fPCLK/2
   
对于主模式和从模式都可实现更快的通信
   
对于主模式和从模式都可通过硬件或软件进行 NSSCS管脚) 管理:动态切换主 /从操作
    可编程的时钟极性和相位
   
可编程的数据顺序,最先移位 MSB LSB
       可触发中断的专用发送和接收标志
    SPI 总线忙状态标志
   
SPI TI 模式
   
用于确保可靠通信的硬件 CRC 功能:
在发送模式下可将 CRC 值作为最后一个字节发送
根据收到的最后一个字节自动进行 CRC 错误校验
可触发中断的主模式故障、上溢和 CRC 错误标志
具有 DMA 功能的 1 字节发送和接收缓冲器:发送和接收请求

2、SPI控制器框架分析

SPI接口:STM32的SPI控制器有多个SPI接口,每个SPI接口可以连接多个SPI从设备。每个SPI接口都包含用于发送和接收数据的寄存器。SPI接口提供了多种配置选项,如时钟极性、时钟相位、数据位数等。

主设备和从设备:在SPI通信中,系统中的一个设备充当主设备,它发起和控制数据传输。其他设备则充当从设备,它们响应主设备的命令并提供数据。主设备通过选择从设备来确定与之通信的目标。

缓冲器和寄存器:SPI控制器使用缓冲器和寄存器来存储发送和接收的数据。主设备通过将数据写入发送缓冲器来发送数据,从设备通过读取接收缓冲器来接收数据。SPI控制器还包含用于控制和配置的寄存器。

时钟设置:SPI控制器使用时钟信号来同步数据传输。时钟信号由主设备控制,可以通过配置时钟极性和相位来设置时钟信号的工作方式。

SPI控制器的工作流程如下:

  1. 主设备选择从设备并开始传输。
  2. 主设备将数据写入发送缓冲器。
  3. 主设备发送时钟信号,从设备接收数据。
  4. 从设备将接收到的数据存储到接收缓冲器。
  5. 主设备继续发送数据并重复上述过程,直到传输完成。

3、相关寄存器

SPI 控制寄存器 1 (SPI_CR1) (不用于 I 2 S 模式)
SPI 数据寄存器 (SPI_DR)
SPI 状态寄存器 (SPI_SR)

SR寄存器用于提示当前能不能发生或者接收下一个字节的数据

等到发送缓冲区为空就可以继续发下一个字节的数据

等到接收缓冲区为空就可以继续接收下一个字节的数据

三、W25Q64芯片

1、芯片介绍

W25Q64 (64M-bit) , W25Q16(16M-bit) 和 W25Q32(32M-bit) 是为系统提供一个最小的空间、引脚和功耗的存储器解决方案的串行 Flash 存储器。 25Q 系列比普通的串行 Flash 存储器更灵活,性能更优越。基于双倍/四倍的 SPI,它们能够可以立即完成提供数据给 RAM, 包括存储声音、文本和数据。芯片支持的工作电压 2.7V 3.6V,正常工作时电流小于 5mA,掉电时低于 1uA。所有芯片提供标准的封装。W25Q64/16/32 由每页 256 字节组成。 每页的 256 字节用一次页编程指令即可完成。每次可以擦 16 页(1个扇区)、 128 页(32KB 块)、 256 页( 64KB 块)和全片擦除。

256字节 = 1页

16页 = 1个扇区

16个扇区 =  1个块

W25Q64 的内存空间结构: 一页 256 字节, 4K(4096 字节) 为一个扇区, 16 个扇区为 1 块, 容量为 8M 字节, 共有 128 个块, 2048 个扇区。

FLASH: 可读可写,只能写入0不能写入1,只有把1变0的能力

擦除的概念:写数据之前要先擦除,擦除就是全部数据变为1

1111 1111

1010 1010

当前W25Q64这个芯片最少擦除4096字节(就是一个扇区),然后去使用

具有唯一的 64 位识别序列号。

● SPI 串行存储器系列                                          ●灵活的 4KB 扇区结构
-W25Q64:64M 位/8M 字节                                     -统一的扇区擦除( 4K 字节)

-W25Q16:16M 位/2M 字节                                     -块擦除( 32K 和 64K 字节)
-W25Q32:32M 位/4M 字节                                     -一次编程 256 字节

-每 256 字节可编程页                                            -至少 100,000 写/擦除周期
-数据保存 20 年

2、芯片管脚说明

CS -- 片选

DO -- 数据输出(MISO)

DI -- 数据输入(MOSI)

Clk -- 时钟线

3、芯片工作原理

W25Q64/16/32兼容的SPI 总线包含四个信号:串行时钟( CLK)、片选端( /CS)、串行数据输入( DI)和串行数据输出( DO)。标准的 SPI 用 DI 输入引脚在 CLK 的上升沿连续的写命令、地址或数据到芯片内。 DO 输出在 CLK 的下降沿从芯片内读出数据或状态。
    支持 SPI 总线的工作模式 0( 0, 0)和 3( 1, 1)。模式 0 和模式 3 的主要区别在于常态时的 CLK
信号,当 SPI 主机已准备好数据还没传输到串行 Flash 中,对于模式 0 CLK 信号常态为低.串行时钟输入引脚为串行输入和输出操作提供时序。(见 SPI 操作)设备数据传输是从高位开始, 数据传输的格式为8bit, 数据采样从第二个时间边沿开始, 空闲状态时, 时钟线 clk 为高电平。
当前这个器件可用的SPI时序符合前面所讲到的模式0和模式3
模式0/3:上升沿接收数据,下降沿发送数据
模式1/2:上升沿发送数据,下降沿接收数据
支持模式0的一般就会支持模式3,支持模式1的一般就会支持模式2

4、芯片操作时序

读ID的时序

拉低片选线(CS)

DI线就是代表代码里需要发送的数据

DO线就代表代码里接收到的数据

读取制造商/设备 ID 指令可以读取,制造商 ID 和特定的设备 ID。 读取之间, 拉低 CS 片选信号, 接着发送指令代码“90h” , 紧随其后的是一个 24 位地址(A23-A0)000000h。之后, 设备发出华邦子制造商 ID(EFh) 和设备ID(w25q64 为 16h)。如果 24 位地址设置为 000001 h 的设备 ID 会先发出,然后跟着制造商 ID。制造商和设备 ID 可以连续读取。完成指令后, 片选信号/ CS 拉高。

写使能

拉低片选

接着发0x06的写使能指令

拉高片选

读状态寄存器

先拉低片选

接着发05这条指令代表存储芯片当前需要去读状态寄存器1

接下来就可以读回来状态寄存器的8个位了

拉高片选

扇区擦除

扇区擦除指令可以擦除指定一个扇区(4 k 字节)内所有数据, 将内存空间恢复到 0xFF 状态。 写入扇区擦除指令之前必须执行设备写使能(发送设备写使能指令 0x06)并判断状态寄存器(状态寄存器位最低位必须等于 0 才能操作)发送的扇区擦除指令前, 先拉低/ CS, 接着发送扇区擦除指令码”20 h”, 和 24 位地址(A23-A0), 地址发送完毕后,拉高片选线 CS/,, 并判断状态位,等待擦除结束。 擦除一个扇区的最少需要 150ms 时间。

写使能

判断状态寄存器的第0位

先拉低片选

发送0x20的指令

发送24位的地址(当前你要擦除的地址是哪里--首地址),这个地址只能是4096的倍数

拉高片选线

页编程指令

页编程指令允许从一个字节到 256 字节的数据编程(一页)(编程之前必须保证内存空间是 0XFF--要先擦除)。允许写入指令之前,必须先发送设备写使能指令。 写使能开启后, 设备才能接收编程指令。 开启页编程先拉底/ CS, 然后发送指令代码“02 h”, 接着发送一个 24 位地址(开始存放数据的地址)(A23-A0)(发送 3 次,每次 8 位) 和至少一个数据字节(数据字节不能超过 256字节)。 数据字节发送完毕, 需要拉高片选线 CS/,并判断状态位, 等待写入结束。进行页编程时,如果数据字节数超过了 256 字节, 地址将自动回到页的起始地址, 覆盖掉之前的数据。 在某些情况下,数据字节小于 256 字节(同一页内), 也可以正常对其他字节存放, 不会有任何影响。如果存放超过 256 字节的数据, 需要分次编程存放。

写使能

拉低片选

发送页编程指令(0x02)

发送24位的地址

发送需要存下来的数据(不能超过当前页的最大地址,如果超过则会回到当前页的起始地址开始存放)

拉高片选

判断当前写入完成没有(读状态寄存器1的第0位)

读数据

读取数据指令允许按顺序读取一个字节的内存数据。当片选 CS/拉低之后, 紧随其后是一个 24 位的地址(A23-A0)(需要发送 3 次, 每次 8 个字节, 先发高位)。芯片收到地址后,将要读的数据按字节大小转移出去, 数据是先转移高位,对于单片机,时钟下降沿发送数据,上升沿接收数据。 读数据时, 地址会自动增加允许连续的读取数据这意味着读取整个内存的数据,只要用一个指令就可以读完。 数据读取完成之后, 片选信号/ CS 拉高。读取数据的指令序列,如上图所示。 如果一个读数据指令而发出的时候, 设备正在擦除扇区,或者(忙= 1), 该读指令将该读指令将被忽略,也不会对当前周期有什么影响。

拉低片选

先发一个0x03的指令

发送要从哪个地址开始读取数据(24位的地址)

再去读取数据

拉高片选

四、模拟SPI

#include "spi.h"

/************************************
函数功能:初始化SPI的管脚
函数形参:void
函数返回值:void
函数说明:CS -- 	PB14: 推挽输出(可以选中也可以释放--有低电平也有高电平)
MISO -- PA6: 浮空输入
MOSI -- PA7: 推挽输出
SCK --	PA5: 推挽输出
作者:
日期:
************************************/
void Spi_Port_Init(void)
{
	//打开GPIOA和GPIOB的时钟
	RCC->AHB1ENR |= 0x3 << 0;
	
	//配置输出
	//配置的是PA5--SCK
	GPIOA->MODER &= ~(0X3 << 10);
	GPIOA->MODER |= (0X1 << 10);
	
	//配置的是PA7--MOSI
	GPIOA->MODER &= ~(0X3 << 14);
	GPIOA->MODER |= (0X1 << 14);
	
	//配置的是PB14--CS
	GPIOB->MODER &= ~(0X3 << 28);
	GPIOB->MODER |= (0X1 << 28);
	
	//配置输入
	GPIOA->MODER &= ~(0X3 << 12);
	
	//把速度配置为50Mhz
	GPIOA->OSPEEDR &= ~(0X3F << 10);
	GPIOA->OSPEEDR |= (0X2A << 10);
}

/************************************
函数功能:模拟SPI来读写数据
函数形参:u8 data -- 需要发送的数据
函数返回值:u8 -- 接收回来的数据可以通过返回值给调用者
函数说明:data = 1011 0011
高位先发
下降沿发送数据,上升沿接收数据
作者:
日期:
************************************/
u8 Spi_Change_Data(u8 data)
{
	u8 rec_data = 0;
	u8 i = 0;
	for(i = 0; i < 8; i++)
	{
		SCK_L;//代表产生下降沿可以开始发送数据
		//1011 0011 & 
		//0100 0000
		//spi不需要加延时
		if(data & (0x80 >> i))
		{
			MOSI_H;
		}
		else
		{
			MOSI_L;
		}
		SCK_H;//代表产生上升沿可以开始接收数据
		rec_data <<= 1;//可以空出最低位
		if(MISO)
		{
			rec_data |= 1;
		}
	}
	return rec_data;
}
#ifndef __SPI_H_
#define __SPI_H_

#include "stm32f4xx.h"
#include "io_bit.h"

#define SCK_H  	(GPIOA->ODR |= 0X1 << 5)
#define SCK_L  	(GPIOA->ODR &= ~(0X1 << 5))
#define MOSI_H  (GPIOA->ODR |= 0X1 << 7)
#define MOSI_L  (GPIOA->ODR &= ~(0X1 << 7))
#define CS_H  	(GPIOB->ODR |= 0X1 << 14)
#define CS_L 	(GPIOB->ODR &= ~(0X1 << 14))
#define MISO    PAin(6)


void Spi_Port_Init(void);
u8 Spi_Change_Data(u8 data);
#endif

五、SPI控制器

1、硬件设计

CS: 推挽输出(可以选中也可以释放--有低电平也有高电平)

MISO: 浮空输入

MOSI: 推挽输出

SCK : 推挽输出

2、软件设计

GPIO口配置

SPI配置

#include "spi.h"

/************************************
函数功能:初始化SPI的管脚
函数形参:void
函数返回值:void
函数说明:CS -- 	PB14: 推挽输出(可以选中也可以释放--有低电平也有高电平)
MISO -- PA6: 浮空输入
MOSI -- PA7: 推挽输出
SCK --	PA5: 推挽输出
作者:li
日期:2021.8.6
************************************/
void Spi_Port_Init(void)
{
    //打开GPIOA和GPIOB的时钟
	RCC->AHB1ENR |= 0x3 << 0;
	
	//PA5-7配置为复用功能
	GPIOA->MODER &= ~(0X3F << 10);
	GPIOA->MODER |= (0X2A << 10);
	
	//复用到SPI1
	GPIOA->AFR[0] &= ~(0XFFF << 20);
	GPIOA->AFR[0] |= (0X555 << 20);
	
	//把速度配置为50Mhz
	GPIOA->OSPEEDR &= ~(0X3F << 10);
	GPIOA->OSPEEDR |= (0X2A << 10);
	
	//配置的是PB14--CS
	GPIOB->MODER &= ~(0X3 << 28);
	GPIOB->MODER |= (0X1 << 28);
	
	
	//打开SPI1的时钟
	RCC->APB2ENR |= 0X1 << 12;
	
	SPI1->CR1 = 0;
	/*
		选择双线单向通信数据模式
		为发送 /接收选择 8 位数据帧格式
		全双工(发送和接收)
		先发送 MSB
		fPCLK/2
		下降沿发送数据,上升沿接收数据
	*/
	
	//使能软件从器件管理
	SPI1->CR1 |= 0X3 << 8;
	
	//主模式
	SPI1->CR1 |= 0X1 << 2;
	
	//使能外设
	SPI1->CR1 |= 0X1 << 6;
}

/************************************
函数功能:
函数形参:u8 data -- 需要发送的数据
函数返回值:u8 -- 接收回来的数据可以通过返回值给调用者
函数说明:data = 1011 0011
高位先发
下降沿发送数据,上升沿接收数据
作者:
日期:
************************************/
u8 Spi_Change_Data(u8 data)
{
	u8 rec_data = 0;
	while(!(SPI1->SR & 0X1 << 1))//当前发送缓冲区为空才能跳出循环
	{
			
	}
	SPI1->DR = data;//发送数据
	
	while(!(SPI1->SR & 0X1 << 0))//当前接收缓冲区为空才能跳出循环
	{
			
	}
	rec_data = SPI1->DR;//接收数据
	return rec_data;
}
#ifndef __SPI_H_
#define __SPI_H_

#include "stm32f4xx.h"
#include "io_bit.h"

#define SCK_H  	(GPIOA->ODR |= 0X1 << 5)
#define SCK_L  	(GPIOA->ODR &= ~(0X1 << 5))
#define MOSI_H  (GPIOA->ODR |= 0X1 << 7)
#define MOSI_L  (GPIOA->ODR &= ~(0X1 << 7))
#define CS_H  	(GPIOB->ODR |= 0X1 << 14)
#define CS_L 	(GPIOB->ODR &= ~(0X1 << 14))
#define MISO    PAin(6)


void Spi_Port_Init(void);
u8 Spi_Change_Data(u8 data);
#endif
#include "w25q64.h"

/************************************
函数功能:读厂商ID
函数形参:void
函数返回值:void
函数说明:发送的指令是0x90
正常接收到的是0xef16
作者:
日期:
************************************/
void Read_ID(void)
{
	u16 w25q64_id = 0;
	CS_L;//拉低CS的片选信号也就是选中这一个芯片
	Spi_Change_Data(0x90);//发送读ID的指令
	
	//发送24位的0
	Spi_Change_Data(0);
	Spi_Change_Data(0);
	Spi_Change_Data(0);
	
	w25q64_id = Spi_Change_Data(0);//得到厂商ID为EFh,此时发什么不重要
	w25q64_id <<= 8;//把接收到的数据存放到16位数据中的高8位
	w25q64_id |= Spi_Change_Data(0xff);
	CS_H;//拉高片选,表示这一次通信结束
	printf("%#x\r\n",w25q64_id);	
}

/************************************
函数功能:写使能
函数形参:void
函数返回值:void
函数说明:发送的指令是0x06
作者:
日期:
************************************/
void Write_Enable(void)
{
	CS_L;
	Spi_Change_Data(0X06);
	CS_H;
}

/************************************
函数功能:读状态寄存器1
函数形参:void
函数返回值:u8
函数说明:发送的指令是0x05
作者:
日期:
************************************/
u8 Read_Register1(void)
{
	u8 reg = 0;
	CS_L;
	Spi_Change_Data(0X05);
	reg = Spi_Change_Data(0);
	CS_H;
	return reg;
}

/************************************
函数功能:扇区擦除
函数形参:u32 addr
函数返回值:void
函数说明:发送的指令是0x20
地址只能是4096的倍数
00000000 00000000 00010000 00000000
作者:
日期:
************************************/
void Sector_Erase(u32 addr)
{
	u8 *p = (u8*)&addr;//现在是得到32位里的最低8位
	u8 ret;
	Write_Enable();
	//直到不忙才可以往下执行
	do
	{
		ret = Read_Register1();
	}while(ret & 0x1 << 0);
	CS_L;
	Spi_Change_Data(0x20);
	
	//发送这24位的地址数据
	Spi_Change_Data(p[2]);
	Spi_Change_Data(p[1]);
	Spi_Change_Data(p[0]);
	
	CS_H;
	
	//直到不忙才可以往下执行
	do
	{
		ret = Read_Register1();
	}while(ret & 0x1 << 0);	
}


/************************************
函数功能:页编程
函数形参:u32 addr--从这一页的哪个位置开始写入数据
u8 *str, -- 需要写入的数据
u8 len --数据的长度
函数返回值:void
函数说明:发送的指令是0x02
当前擦除了哪一个扇区就可以在当前扇区的某一页写入数据
最大写入256字节
作者:
日期:
************************************/
void Page_Program(u32 addr,u8 *str,u8 len)
{
	u8 *p = (u8*)&addr;//现在是得到32位里的最低8位
	u8 ret;
	Write_Enable();
	
	//直到不忙才可以往下执行
	do
	{
		ret = Read_Register1();
	}while(ret & 0x1 << 0);
	
	CS_L;
	Spi_Change_Data(0x02);
	
	//发送这24位的地址数据
	Spi_Change_Data(p[2]);
	Spi_Change_Data(p[1]);
	Spi_Change_Data(p[0]);
	
	while(len--)
	{
		Spi_Change_Data(*str);
		str++;
	}
	CS_H;
	
	//直到不忙才可以往下执行
	do
	{
		ret = Read_Register1();
	}while(ret & 0x1 << 0);

}

/************************************
函数功能:读数据
函数形参:u32 addr--从这一页的哪个位置开始读取数据
u8 *str, -- 读取到的数据存放的地方
u8 len -- 数据的长度
函数返回值:void
函数说明:发送的指令是0x03
可以一直读到芯片最后
作者:
日期:
************************************/
void Read_Data(u32 addr,u8 *str,u8 len)
{
	u8 *p = (u8*)&addr;//现在是得到32位里的最低8位
	u8 ret;
	CS_L;
	Spi_Change_Data(0x03);
	
	//发送这24位的地址数据
	Spi_Change_Data(p[2]);
	Spi_Change_Data(p[1]);
	Spi_Change_Data(p[0]);
	
	while(len--)
	{
		*str = Spi_Change_Data(0);
		str++;
	}
	CS_H;
}

/************************************
函数功能:跨页写
函数形参:u32 addr--从这一页的哪个位置开始读取数据
u8 *str, -- 读取到的数据存放的地方
u8 len -- 数据的长度
函数返回值:void
函数说明:一次性可以发送多于256字节的数据到FLASH里
问题:当前的页编程不能超过本页的最大地址
解决:
需要判断什么时候到达本页的最大地址了
如果已经到了则地址自动跳转到下一页
		
判断这一页还剩多少空间,如果不够再去换页写入
				
作者:
日期:
************************************/
void Page_AutoWrite(u32 addr,u8 *str,u8 len)//addr = 0,len = 289
{
	u16 less_len = 0;
	
	less_len = 256 - addr % 256;//判断任意一页还剩多少空间可以写
	
	//如果剩余的空间足够存下要写入的数据则写入数据长度为当前数据长度
	if(less_len>=len)
	{
		less_len = len;
	}
	
	while(1)
	{
		//写入数据
		Page_Program(addr,str,less_len);
		
		//判断当前页剩余空间是不是已经等于要写入数据的长度,如果相等则不需要换页
		if(less_len == len)
		{
			break;
		}
		//"4564654"
		addr +=  less_len;//代表当前地址的偏移
		str += less_len;//加上已经写入的长度可以去到未写入的数据的位置
		len = len - less_len;//代表未写入的数据长度
		less_len = 256;//当前如果能跑到这一步,肯定是到下一页的起始地址了
		
		if(less_len>=len)//1024
		{
			less_len = len;
		}
	}
}
#ifndef __W25Q64_H_
#define __W25Q64_H_

#include "stm32f4xx.h"
#include "io_bit.h"
#include "spi.h"
#include "stdio.h"

void Read_ID(void);
void Sector_Erase(u32 addr);
void Page_Program(u32 addr,u8 *str,u8 len);
void Read_Data(u32 addr,u8 *str,u8 len);
void Page_AutoWrite(u32 addr,u8 *str,u8 len);


#endif

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

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

相关文章

Kotlin Flow 操作符

前言 Kotlin 拥有函数式编程的能力&#xff0c;使用Kotlin开发&#xff0c;可以简化开发代码&#xff0c;层次清晰&#xff0c;利于阅读。 然而Kotlin拥有操作符很多&#xff0c;其中就包括了flow。Kotlin Flow 如此受欢迎大部分归功于其丰富、简洁的操作符&#xff0c;巧妙使…

【头歌系统数据库实验】实验8 SQL的复杂多表查询-2

目录 第1关&#xff1a;基于派生表查询每个队员解答中超过他平均memory的user_id及题目编号problem_id 第2关&#xff1a;用ANY/ALL实现查询2019级选手&#xff08;user_id前4位为2019&#xff09;满足比2020级其中一个选手注册时间早即可的选手 第3关&#xff1a;用聚集查询…

【MySQL】MySQL 在 Centos 7环境安装教程

文章目录 1.卸载不要的环境2.检查系统安装包3.获取mysql官方yum源4.安装mysql yum 源&#xff0c;对比前后yum源5.安装mysql服务6.查看配置文件和数据存储位置7.启动服务和查看启动服务8.登录9.配置my.cnf 1.卸载不要的环境 先检查是否有mariadb存在 ps ajx |grep mariadb如果…

【Linux】系统初识之冯诺依曼体系结构与操作系统

&#x1f440;樊梓慕&#xff1a;个人主页 &#x1f3a5;个人专栏&#xff1a;《C语言》《数据结构》《蓝桥杯试题》《LeetCode刷题笔记》《实训项目》《C》《Linux》 &#x1f31d;每一个不曾起舞的日子&#xff0c;都是对生命的辜负 目录 前言 1.冯诺依曼体系结构 2.操作…

文章解读与仿真程序复现思路——电力系统自动化EI\CSCD\北大核心《考虑电力-交通交互的配电网故障下电动汽车充电演化特性》

这个标题涉及到电力系统、交通系统和电动汽车充电的复杂主题。让我们逐步解读&#xff1a; 考虑电力-交通交互的配电网故障&#xff1a; 电力-交通交互&#xff1a; 指的是电力系统和交通系统之间相互影响、相互关联的关系。这可能涉及到电力需求对交通流量的影响&#xff0c;反…

爬虫解析-jsonpath (六)

jsonpath只能解析本地文件 jsonpath的使用&#xff1a; obj json.load(open(.json文件,r,encodingutf-8))place_name jsonpath.jsonpath(obj, json语法) 目录 1.安装jsonpath 2.Xpath和jsonpath的语法对比 练习&#xff1a;使用jsonpath解析JSON文件 3.使用jsonpath抓取…

C语言实现快速排序

完整代码&#xff1a; #include<stdio.h>//用第一个元素将待排序序列划分成左右两个部分&#xff0c;返回排序后low的位置&#xff0c;即枢轴的位置 int partition(int arr[],int low,int high){//让待排序序列中的第一个元素成为基准int pivotarr[low];//lowhigh代表一…

计算机科学与技术认识实习【报告】

一、实习目的 此次认识实习主要面对计算机科学与技术专业的同学&#xff0c;了解专业在未来的发展趋势&#xff0c;通过观看公司的介绍视频和技术发展情况招聘信息后的感想和学习体会等多种方式&#xff0c;使我们了解本专业相关领域的发展现状&#xff0c;让我们在校园内课堂上…

【动态规划】【广度优先】LeetCode2258:逃离火灾

作者推荐 本文涉及的基础知识点 二分查找算法合集 动态规划 二分查找 题目 给你一个下标从 0 开始大小为 m x n 的二维整数数组 grid &#xff0c;它表示一个网格图。每个格子为下面 3 个值之一&#xff1a; 0 表示草地。 1 表示着火的格子。 2 表示一座墙&#xff0c;你跟…

极智一周 | AI 算力国产化、通义开源、Gemini、鸿蒙、蔚来 And so on

欢迎关注我的公众号 [极智视界]&#xff0c;获取我的更多技术分享 大家好&#xff0c;我是极智视界&#xff0c;带来本周的 [极智一周]&#xff0c;关键词&#xff1a;AI 算力国产化、通义开源、Gemini、鸿蒙、蔚来 And so on。 邀您加入我的知识星球「极智视界」&#xff0c;…

【Linux】make/Makefile --- 自动化构建项目的工具

目录 一、make/Makefile的简单使用 二、Makefile 的语法规则 三、实现的原理 3.1 make/Makefile识别文件新旧 3.2 .PHONY修饰的伪目标总是被执行 3.3 make/Makefile是具有依赖性的推导能力的 四、语法技巧 五、注意事项 Linux中自动化构建项目最简单的方式&#xff1a;…

Linux系统---简易伙伴系统

顾得泉&#xff1a;个人主页 个人专栏&#xff1a;《Linux操作系统》 《C/C》 《LeedCode刷题》 键盘敲烂&#xff0c;年薪百万&#xff01; 一、题目要求 1.采用C语言实现 2.伙伴系统采用free_area[11]数组来组织。要求伙伴内存最小为一个页面&#xff0c;页面大小为4KB…

C语言习题

写一个函数&#xff0c;输入一个四位数字&#xff0c;要求输出这四个数字字符&#xff0c;但每两个数字间空一个空格。如输入1990&#xff0c;输出1 9 9 0 如下&#xff1a; #include<stdio.h> void Print(int n) { if(n>9) { Print(n/10); } printf("%d "…

ssm的健身房预约系统(有报告)。Javaee项目。ssm项目。

演示视频&#xff1a; ssm的健身房预约系统&#xff08;有报告&#xff09;。Javaee项目。ssm项目。 项目介绍&#xff1a; 采用M&#xff08;model&#xff09;V&#xff08;view&#xff09;C&#xff08;controller&#xff09;三层体系结构&#xff0c;通过Spring Spring…

【trino权威指南】使用trino详解:trino client安装、查询sql、DBeaver连接trino、java通过JDBC连接trino

文章目录 一. Trino CLI1. 安装client2. 使用client执行sql 二. JDBC driver 连接Trino1. 通过DBeaver用户界面连接2. JDBC Driver in java2.1. 环境配置2.2. 注册和配置driver2.3. 连接参数2.4. 查询例子 一. Trino CLI 1. 安装client Trino CLI提供了一个基于终端的交互式s…

H264之NALU结构详解

摘要&#xff1a;本文详细描述了AVC的NALU的码流结构&#xff0c;以及各个层面上NALU详细的构成。   关键字&#xff1a;AVC&#xff0c;NALU 1 NALU简介 NAL层即网络抽象层&#xff08;Network Abstraction Layer&#xff09;&#xff0c;是为了方便在网络上传输的一种抽象…

tomcat篇---第四篇

系列文章目录 文章目录 系列文章目录前言一、为什么我们将tomcat称为Web容器或者Servlet容器 ?二、tomcat是如何处理Http请求流程的?三、tomcat结构目录有哪些?前言 前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站,这…

Mysql索引一篇就够了

索引 定义 索引是对数据库表中一列或者多列的值进行排序的结构。 目的 数据库索引好比一本书的目录&#xff0c;提高查询效率。但是为表设置索引要付出相应的代价&#xff1a; 增加了数据库的存储空间 在插入和修改时需花费更多的时间&#xff08;因为索引也要随之变动&#…

带有 RaspiCam 的 Raspberry Pi 监控和延时摄影摄像机

一、说明 一段时间以来&#xff0c;我一直想构建一个运动激活且具有延时功能的树莓派相机&#xff0c;但从未真正找到我喜欢的案例。我在thingiverse上找到了这个适合树莓派和相机的好案例。它是为特定的鱼眼相机设计的&#xff0c;但从模型来看&#xff0c;我拥有的廉价中国鱼…

【基于Python的二手车数据可视化平台的设计与实现】

基于Python的二手车数据可视化平台的设计与实现 前言数据获取与处理网络爬虫数据存储 可视化平台的设计与实现Flask框架数据可视化 创新点结语 前言 随着社会的不断发展&#xff0c;二手车市场也逐渐成为一个备受关注的领域。为了更好地为二手车的买家和卖家提供信息&#xff…