stm32中的SPI
SPI的简介
文章目录
- SPI的简介
- 物理层
- 协议层
- 基本通讯过程
- 起始和终止信号
- 数据有效性
- CPOL/CPHA及通讯模式
- STM3的SPI特性及架构
- 通讯引脚
- 时钟控制逻辑
- 数据控制逻辑
- 整体控制逻辑
- 通讯过程
- 代码配置实现
- 指令集
- 结构体的定义
- SPI时钟信号的定义
- SPI端口定义
- SPI命令
- flash驱动代码
- 初始化代码(配置端口)
- 配置SPI模式代码
- 发送并接受一个字节
- 读取字节
- 读取ID号
- FLASH写入使能
- 等待FLASH内部时序操作完成
- 擦除FLASH指定扇区
物理层
SPI一共三条总线
SPI总线 | 功能 |
---|---|
SS( Slave Select) | 选从机 |
SCK (Serial Clock) | 时钟信号线,用于通讯数据同步。它由通讯主机产生,决定了通讯的速率 |
MOSI (Master Output,Slave Input) | 主设备输出/从设备输入引脚。这条线上数据的方向为主机到从机。 |
MISO (Master Input,,Slave Output) | 主设备输入/从设备输出引脚。在这条线上数据的方向为从机到主机。 |
协议层
基本通讯过程
- NSS为低电平时候才有效
- SCK每一个周期MOSI和MISO传输一位数据
起始和终止信号
- 起始:高变低
- 终止:低变高
数据有效性
- SPI 使用 MOSI 及 MISO 信号线来传输数据,使用 SCK 信号线进行数据同步。MOSI 及 MISO 数据线在 SCK 的每个时钟周期传输一位数据,且数据输入输出是同时进行的。数据传输时,MSB先行或 LSB 先行并没有作硬性规定,但要保证两个 SPI 通讯设备之间使用同样的协定,一般都会采用图 SPI 通讯时序 中的 MSB 先行模式。
- 即在 SCK 的下降沿时刻,MOSI 及 MISO 的数据有效,高电平时表示数据“1”,为低电平时表示数据“0”。在其它时刻,数据无效,MOSI 及 MISO 为下一次表示数据做准备。SPI 每次数据传输可以 8 位或 16 位为单位,每次传输的单位数不受限制。
CPOL/CPHA及通讯模式
CPHA:当 CPHA=0 时,MOSI 或 MISO 数据线上的信号将会在SCK 时钟线的“奇数边沿”被采样。当 CPHA=1 时,数据线在 SCK 的“偶数边沿”采样。一个边沿被设置为采样后另一个边沿只能为读取数据。
CPOL:控制SCK空闲时刻的电平,0为低电平,1为高电平。
STM3的SPI特性及架构
通讯引脚
时钟控制逻辑
由波特率发生器根据“控制寄存器 CR1”中的 BR[0:2] 位控制
数据控制逻辑
SPI 的 MOSI 及 MISO 都连接到数据移位寄存器上,数据移位寄存器的数据来源及目标接收、发送缓冲区以及 MISO、MOSI 线。
当从外部接收数据的时候,数据移位寄存器把数据线采样到的数据一位一位地存储到“接收缓冲区”中。
通过写 SPI 的“数据寄存器 DR”把数据填充到发送缓冲区中,通讯读“数据寄存器 DR”,可以获取接收缓冲区中的内容。
DR[15:0]:数据寄存器 (Data register) 待发送或者已经收到的数据
数据寄存器对应两个缓冲区:一个用于写(发送缓冲);另外一个用于读(接收缓冲)。写操作将数据写到发送缓冲区;读操作将返回接收缓冲区里的数据。
**对SPI模式的注释:**根据SPI_CR1的DFF位对数据帧格式的选择,数据的发送和接收可以是8位或者16位的。为保证正确的操作,需要在启用SPI之前就确定好数据帧格式。
对于8位的数据,缓冲器是8位的,发送和接收时只会用到SPI_DR[7:0]。在接收时,SPI_DR[15:8]被强制为0。
对于16位的数据,缓冲器是16位的,发送和接收时会用到整个数据寄存器,即SPI_DR[15:0]。
**其中数据帧:**长度可以通过“控制寄存器 CR1”的“DFF 位”配置成 8 位及 16 位模式;配置“LSBFIRST 位”可选择 MSB 先行还是 LSB 先行。
整体控制逻辑
整体控制逻辑负责协调整个 SPI 外设,控制逻辑的工作模式根据我们配置的“控制寄存器(CR1/CR2)”的参数而改变基本的控制参数包括前面提到的 SPI 模式、波特率、LSB 先行、主从模式、单双向模式等等。
通讯过程
(1) 控制 NSS 信号线,产生起始信号 (图中没有画出);
(2) 把要发送的数据写入到“数据寄存器 DR”中,该数据会被存储到发送缓冲区;
(3) 通讯开始,SCK 时钟开始运行。MOSI 把发送缓冲区中的数据一位一位地传输出去;MISO 则把数据一位一位地存储进接收缓冲区中;
(4) 当发送完一帧数据的时候,“状态寄存器 SR”中的“TXE 标志位”会被置 1,表示传输完一帧,发送缓冲区已空;类似地,当接收完一帧数据的时候,“RXNE 标志位”会被置 1,表示传输完一帧,接收缓冲区非空;
(5) 等待到“TXE 标志位”为 1 时,若还要继续发送数据,则再次往“数据寄存器 DR”写入数据即可;等待到“RXNE 标志位”为 1 时,通过读取“数据寄存器 DR”可以获取接收缓冲区中的内容。假如我们使能了 TXE 或 RXNE 中断,TXE 或 RXNE 置 1 时会产生 SPI 中断信号,进入同一个中断服务函数,到 SPI 中断服务程序后,可通过检查寄存器位来了解是哪一个事件,再分别进行处理。也可以使用 DMA 方式来收发“数据寄存器 DR”中的数据
代码配置实现
指令集
结构体的定义
SPI时钟信号的定义
#define FLASH_SPIx SPI1
#define FLASH_SPI_APBxClock_FUN RCC_APB2PeriphClockCmd
#define FLASH_SPI_CLK RCC_APB2Periph_SPI1
#define FLASH_SPI_GPIO_APBxClock_FUN RCC_APB2PeriphClockCmd
SPI端口定义
#define FLASH_SPI_SCK_PORT GPIOA
#define FLASH_SPI_SCK_PIN GPIO_Pin_5#define FLASH_SPI_MOSI_PORT GPIOA
#define FLASH_SPI_MOSI_PIN GPIO_Pin_7#define FLASH_SPI_MISO_PORT GPIOA
#define FLASH_SPI_MISO_PIN GPIO_Pin_6#if (USE_BD ==1)#define FLASH_SPI_GPIO_CLK RCC_APB2Periph_GPIOA#define FLASH_SPI_CS_PORT GPIOA #define FLASH_SPI_CS_PIN GPIO_Pin_4
#else#define FLASH_SPI_GPIO_CLK (RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOC)#define FLASH_SPI_CS_PORT GPIOC#define FLASH_SPI_CS_PIN GPIO_Pin_0
#endif
SPI命令
#define DUMMY 0x00
#define READ_JEDEC_ID 0x9f
#define ERASE_SECTOR 0x20
#define READ_STATUS 0x05
#define READ_DATA 0x03
#define WRITE_ENABLE 0x06
#define WRITE_DATA 0x02
flash驱动代码
初始化代码(配置端口)
/*** @brief SPII/O配置* @param 无* @retval 无*/
static void SPI_GPIO_Config(void)
{GPIO_InitTypeDef GPIO_InitStructure; /* 使能与SPI 有关的时钟 */FLASH_SPI_APBxClock_FUN ( FLASH_SPI_CLK, ENABLE );FLASH_SPI_GPIO_APBxClock_FUN ( FLASH_SPI_GPIO_CLK, ENABLE );/* MISO MOSI SCK*/GPIO_InitStructure.GPIO_Pin = FLASH_SPI_SCK_PIN;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_Init(FLASH_SPI_SCK_PORT, &GPIO_InitStructure);GPIO_InitStructure.GPIO_Pin = FLASH_SPI_MOSI_PIN;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_Init(FLASH_SPI_MOSI_PORT, &GPIO_InitStructure);GPIO_InitStructure.GPIO_Pin = FLASH_SPI_MISO_PIN;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; GPIO_Init(FLASH_SPI_MISO_PORT, &GPIO_InitStructure);//初始化CS引脚,使用软件控制,所以直接设置成推挽输出 GPIO_InitStructure.GPIO_Pin = FLASH_SPI_CS_PIN;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_Init(FLASH_SPI_CS_PORT, &GPIO_InitStructure);FLASH_SPI_CS_HIGH;
}
配置SPI模式代码
static void SPI_Mode_Config(void)
{SPI_InitTypeDef SPI_InitStructure; SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_2 ;//SPI 使用模式3SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge ;SPI_InitStructure.SPI_CPOL = SPI_CPOL_High ;SPI_InitStructure.SPI_CRCPolynomial = 0;//不使用CRC功能,数值随便写SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex ;//双线全双工SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB ;SPI_InitStructure.SPI_Mode = SPI_Mode_Master ;SPI_InitStructure.SPI_NSS = SPI_NSS_Soft ; SPI_Init(FLASH_SPIx,&SPI_InitStructure); //写入配置到寄存器SPI_Cmd(FLASH_SPIx,ENABLE);//使能SPI}
SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge ;
SPI_InitStructure.SPI_CPOL = SPI_CPOL_High ;
这段代码设置了 SPI 的时钟相位(Clock Phase),具体来说,SPI_CPHA_2Edge
表示在第二个时钟沿(第二个边沿)采样数据。让我们分解这个设置:
SPI_CPHA
是 SPI_InitTypeDef 结构体中的一个成员,用于配置 SPI 的时钟相位。SPI_CPHA_2Edge
是一个预定义的常量,它表示在第二个时钟沿(2Edge)采样数据。
时钟相位(Clock Phase)决定了在时钟的哪个边沿数据应该被采样或变更。在 SPI 通信中,时钟相位通常有两个选项:第一个时钟沿(1Edge)和第二个时钟沿(2Edge)。
- 当设置为
SPI_CPHA_1Edge
时,数据在第一个时钟沿(上升沿或下降沿)被采样或变更。 - 当设置为
SPI_CPHA_2Edge
时,数据在第二个时钟沿被采样或变更。
在这个代码片段中,通过设置 SPI_CPHA
为 SPI_CPHA_2Edge
,表明数据在第二个时钟沿被采样。这样的设置通常取决于与 SPI 设备通信的具体协议和要求。
综合一下这段代码是下降沿采样
SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB ;
这段代码设置了 SPI 数据传输的起始位。具体来说,SPI_FirstBit_MSB
表示数据传输的起始位是最高有效位(Most Significant Bit,MSB)。
在 SPI 数据传输中,每个字节都由多个位组成,通常是8位。字节中的最高有效位是二进制表示中的最左边的位,而最低有效位则是最右边的位。
通过设置 SPI_FirstBit
为 SPI_FirstBit_MSB
,代码指定了数据传输时先传输最高有效位,然后依次传输剩余的位。这通常符合大多数 SPI 设备和通信协议的约定,但在某些情况下,可能需要根据具体设备的要求进行调整。
发送并接受一个字节
static uint32_t SPI_TIMEOUT_UserCallback(uint8_t errorCode)
{/* Block communication and all processes */FLASH_ERROR("SPI 等待超时!errorCode = %d",errorCode);return 0;
}
uint8_t SPI_FLASH_Send_Byte(uint8_t data)
{SPITimeout = SPIT_FLAG_TIMEOUT;//检查并等待至TX缓冲区为空while(SPI_I2S_GetFlagStatus(FLASH_SPIx,SPI_I2S_FLAG_TXE) == RESET){if((SPITimeout--) == 0) return SPI_TIMEOUT_UserCallback(0);}//程序执行到此处,TX缓冲区已空SPI_I2S_SendData (FLASH_SPIx,data);SPITimeout = SPIT_FLAG_TIMEOUT;//检查并等待至RX缓冲区为非空while(SPI_I2S_GetFlagStatus(FLASH_SPIx,SPI_I2S_FLAG_RXNE) == RESET){if((SPITimeout--) == 0) return SPI_TIMEOUT_UserCallback(0);} //程序执行到此处,说明数据发送完毕,并接收到一字字节 return SPI_I2S_ReceiveData(FLASH_SPIx); //返回的数据是他排出的}
这段代码涉及 SPI 数据的发送和接收,以下是代码的主要步骤解释:
-
发送数据:
SPI_I2S_SendData(FLASH_SPIx, data);
通过调用
SPI_I2S_SendData
函数,将数据data
发送到 SPI 设备。 -
等待接收缓冲区非空:
SPITimeout = SPIT_FLAG_TIMEOUT; while(SPI_I2S_GetFlagStatus(FLASH_SPIx, SPI_I2S_FLAG_RXNE) == RESET) {if((SPITimeout--) == 0) return SPI_TIMEOUT_UserCallback(0); }
这部分代码在一个循环中检查 SPI 接收缓冲区是否为非空(
SPI_I2S_FLAG_RXNE
表示接收缓冲区非空)。循环会一直等待,直到接收缓冲区有数据或超时。 -
接收数据:
return SPI_I2S_ReceiveData(FLASH_SPIx);
一旦接收缓冲区非空,就调用
SPI_I2S_ReceiveData
函数从 SPI 设备接收数据,并将其返回。
通过 SPI 发送数据,然后等待接收缓冲区非空,最后从接收缓冲区中读取数据。在这个过程中,使用了超时机制来处理可能的等待超时情况。这样的代码结构常见于需要同步发送和接收数据的 SPI 通信场景。
读取字节
uint8_t SPI_FLASH_Read_Byte(void)
{return SPI_FLASH_Send_Byte(DUMMY);
}
DUMMY可以是任意值,一般是0x00或0xFF,都出来后flash就没有这个数据了(实验得出)
读取ID号
//读取ID号
uint32_t SPI_Read_ID(void)
{uint32_t flash_id;//片选使能FLASH_SPI_CS_LOW;SPI_FLASH_Send_Byte(READ_JEDEC_ID);flash_id = SPI_FLASH_Send_Byte(DUMMY);flash_id <<= 8;flash_id |= SPI_FLASH_Send_Byte(DUMMY); flash_id <<= 8;flash_id |= SPI_FLASH_Send_Byte(DUMMY); FLASH_SPI_CS_HIGH; return flash_id;
}
这段代码实现了通过 SPI 读取设备的 ID 号。以下是代码的主要步骤解释:
-
片选使能:
FLASH_SPI_CS_LOW;
通过将片选信号拉低,使能 SPI 设备。
-
发送读取 JEDEC ID 的命令:
SPI_FLASH_Send_Byte(READ_JEDEC_ID);
通过调用
SPI_FLASH_Send_Byte
函数发送读取 JEDEC ID 的命令。 -
读取 ID 号的三个字节:
flash_id = SPI_FLASH_Send_Byte(DUMMY); flash_id <<= 8; flash_id |= SPI_FLASH_Send_Byte(DUMMY); flash_id <<= 8; flash_id |= SPI_FLASH_Send_Byte(DUMMY);
通过调用
SPI_FLASH_Send_Byte
函数,依次读取三个字节的 ID 号。每次读取一个字节,然后将其左移相应的位数,最终组成一个 32 位的 ID 号。 -
片选失能:
FLASH_SPI_CS_HIGH;
通过将片选信号拉高,失能 SPI 设备。
-
返回读取到的 ID 号:
return flash_id;
将读取到的 32 位 ID 号作为函数的返回值。
这段代码通过 SPI 通信协议与外部设备进行交互,发送读取 JEDEC ID 的命令,接着读取返回的三个字节,最终组成一个完整的 32 位 ID 号。这样的操作通常用于识别连接的外部设备或验证设备的身份。
FLASH写入使能
void SPI_Write_Enable(void)
{//片选使能FLASH_SPI_CS_LOW;//拉低代表被选中SPI_FLASH_Send_Byte(WRITE_ENABLE);//写入赋能命令 FLASH_SPI_CS_HIGH;//表示操作完毕
}
在 SPI (Serial Peripheral Interface) 通信中,CS
通常指的是 Chip Select(芯片选择)信号。Chip Select 是一种用于选择特定从设备的信号,它告诉 SPI 总线上的从设备何时应该响应主设备的通信。
等待FLASH内部时序操作完成
void SPI_WaitForWriteEnd(void)
{uint8_t status_reg = 0;//片选使能FLASH_SPI_CS_LOW;SPI_FLASH_Send_Byte(READ_STATUS);//读取状态命令do{ status_reg = SPI_FLASH_Send_Byte(DUMMY);//获得Flash上寄存器的数据}while((status_reg & 0x01) == 1);FLASH_SPI_CS_HIGH;
}
擦除FLASH指定扇区
void SPI_Erase_Sector(uint32_t addr)
{ SPI_Write_Enable();//片选使能FLASH_SPI_CS_LOW;SPI_FLASH_Send_Byte(ERASE_SECTOR);SPI_FLASH_Send_Byte((addr>>16)&0xff);SPI_FLASH_Send_Byte((addr>>8)&0xff); SPI_FLASH_Send_Byte(addr&0xff); FLASH_SPI_CS_HIGH; SPI_WaitForWriteEnd();}
SPI_FLASH_Send_Byte(ERASE_SECTOR);
写入擦除命令。
SPI_FLASH_Send_Byte((addr>>16)&0xff);SPI_FLASH_Send_Byte((addr>>8)&0xff); SPI_FLASH_Send_Byte(addr&0xff);
地址由三个字节组成所以用这种方法。
读取和写
//读取FLASH的内容
void SPI_Read_Data(uint32_t addr,uint8_t *readBuff,uint32_t numByteToRead)
{//片选使能FLASH_SPI_CS_LOW;SPI_FLASH_Send_Byte(READ_DATA);SPI_FLASH_Send_Byte((addr>>16)&0xff);SPI_FLASH_Send_Byte((addr>>8)&0xff); SPI_FLASH_Send_Byte(addr&0xff); while(numByteToRead--){//这段代码是为了激活时钟信号*readBuff = SPI_FLASH_Send_Byte(DUMMY);readBuff++;}FLASH_SPI_CS_HIGH; }//向FLASH写入内容
//读取FLASH的内容
//写入数据前都要擦除
void SPI_Write_Data(uint32_t addr,uint8_t *writeBuff,uint32_t numByteToWrite)
{SPI_Write_Enable();//片选使能FLASH_SPI_CS_LOW;SPI_FLASH_Send_Byte(WRITE_DATA);SPI_FLASH_Send_Byte((addr>>16)&0xff);SPI_FLASH_Send_Byte((addr>>8)&0xff); SPI_FLASH_Send_Byte(addr&0xff); while(numByteToWrite--){ SPI_FLASH_Send_Byte(*writeBuff);writeBuff++;}FLASH_SPI_CS_HIGH; SPI_WaitForWriteEnd();
}
写入数据前都要擦除!
写入数据前都要擦除!
写入数据前都要擦除!
因为SPI中数据默认为0xFF,需要自己去擦除,不会在写入时自动擦除!
取FLASH的内容
相关文章:

stm32中的SPI
SPI的简介 文章目录 SPI的简介物理层协议层基本通讯过程起始和终止信号数据有效性CPOL/CPHA及通讯模式 STM3的SPI特性及架构通讯引脚时钟控制逻辑数据控制逻辑整体控制逻辑通讯过程 代码配置实现指令集结构体的定义SPI时钟信号的定义SPI端口定义SPI命令 flash驱动代码初始化代码…...

ChatGPT可与自定义GPTs一起使用,智能AI代理时代来啦!
1月31日凌晨,OpenAI在社交平台公布了一个超强新功能,可以在ChatGPT中输入“GPTs名字”的方法,调用多个自定义GPTs一起协同工作。 例如,我想开发一款社交APP,1)可以先用专业分析GPTs做一下市场调研…...

《Numpy 简易速速上手小册》第1章:Numpy 基础(2024 最新版)
文章目录 1.1 创建和操作 Numpy 数组1.1.1 基础知识1.1.2 完整案例:多维数据分析1.1.3 拓展案例 1:时间序列数据1.1.4 拓展案例 2:图像数据处理 1.2 数组的索引和切片1.2.1 基础知识1.2.2 完整案例:时间序列分析1.2.3 拓展案例 1&…...

【美团】SaaS技术部-后端研发工程师(海外业务)
部门介绍 美团餐饮系统为餐饮企业提供一站式IT解决方案,帮助餐饮商户实现从供应链管理、生产管理、前厅管理到外卖的数字化经营。美团餐饮系统不仅打通了餐厅和平台,更帮助餐厅连接客人,让商户更了解顾客需求,在帮助商户做商业决…...

linux安装mongodb数据库启动报错? 都是冰红茶滴水儿
先展示报错信息 网上一大推说是关闭不正确导致的,然后给出的解决方法是 ./mongod -f mongodb.conf --repair吊用没有,还是报错: about to fork child process, waiting until server is ready for connections. forked process: 302226 ERROR: child process failed, exited…...

win11安装wsl作为linux子系统并当作服务器
wsl安装 打开控制面板,找到启用或关闭windows功能 开启windows虚拟机监控平台和适用于Linux的Windows子系统,重启电脑。 打开microsoft store搜索ubuntu,找到合适的版本下载安装 输入wsl -l如下所示,即为安装成功。 安装过程比较…...

户用光伏电站的管理包括哪些内容?需要怎么做?
户用光伏电站在运行后需要进行一系列的管理工作,包括安全、运行、质量和数据等等,从而保证电站的安全、经济、高效运行。 1.安全管理 光伏电站的安全管理是非常重要的,包括电站的防雷、防火、防盗等措施,以及电站内部的安全管理…...

Kafka-服务端-PartitionLeaderSelector、ReplicaStateMachine
PartitionLeaderSelector 通过对前面的分析可知,PartitionMachine将Leader副本选举、确定ISR集合的工作委托给了PartitionLeaderSelector接口实现,PartitionMachine可以专注于管理分区状态。这是策略模式的一种典型的应用场景。 图展示了PartitionLead…...

总结11(数组)
数组 1.为什么需要数组 为了解决大量同类型数据的存储和使用问题 为了模拟现实世界 2. 数组的分类 一维数组 1).怎样定义一维数组 为n个变量连续分配存储空间。 所有的变量数据类型必须相同。 所有变量所占的字节大小必须相等。 例子: int a[5]; &#x…...

扩展学习|大数据分析整合到价值创造的大见解
文献来源:Saggi M K, Jain S. A survey towards an integration of big data analytics to big insights for value-creation[J]. Information Processing & Management, 2018, 54(5): 758-790. 下载链接:链接:https://pan.baidu.com/s/14IGaCOc-plxAiaVhwOgUvA 提取码…...

蓝桥杯---牌型种数
小明被劫持到X赌城,被迫与其他3人玩牌。一副扑克牌(去掉大小王牌,共52张),均匀发给4个人,每个人13张。这时,小明脑子里突然冒出一个问题:如果不考虑花色,只考虑点数,也不考虑自己得到的牌的先后…...

【Linux】VMware Workstation16安装银河麒麟高级服务器操作系统V10 SP3 AMD64
目录 一、麒麟服务器概述 二、安装步骤 设置硬盘大小 完成配置 修改内存 处理器等设备配置 选择直接安装 配置磁盘 网络配置 设置root账号密码 开始安装 启动完成 一、麒麟服务器概述 银河麒麟高级服务器操作系统V10是针对企业级关键业务,适应虚拟化、云…...

中国的茶文化:历史、传统与生活
中国的茶文化:历史、传统与生活 一、引言 茶,这一神奇而古老的饮品,与中国的历史、文化和生活方式紧密相连。中国的茶文化,源远流长,博大精深,是中华文明的重要组成部分。它不仅是一种饮料,更是…...

Oracle函数使用
ROW_NUMBER函数 ROW_NUMBER() OVER(PARTITION BY column1 ORDER BY column2 DESC) -- 根据column1分组按column2降序排序生成序号,序号由小到大,会生成一个唯一的序号 -- 例如column2中有两列值都为1,那他们的序号会有一个在上一个在下ROW_NUMBER() OVER(ORDER BY …...

正则表达式与文本三剑客
目录 一、正则表达式 1. 定义 2. 字符匹配 3. 重复限定符 4. 位置锚点 5. 分组和引用 6. 扩展正则表达式 二、文本三剑客 1. grep 1.1 定义 1.2 语法 1.3 选项 1.4 示例 2. sed 2.1 定义 2.2 通式 2.3 选项 2.4 脚本格式(脚本语法) 2.…...

C语言中的数组操作技巧:提升程序的效率和可读性
1. 概念 数组是C语言中常见且重要的数据结构,在许多应用中都被广泛使用。合理地处理数组操作可以提高程序的效率和可读性。本文将介绍C语言中常用的数组操作方法和技巧,帮助读者优化数组操作并提升程序效果。 2.常用的数组操作方法 2.1 数组的初始化 C…...

202 快乐数
编写一个算法来判断一个数 n 是不是快乐数。 「快乐数」 定义为: 对于一个正整数,每一次将该数替换为它每个位置上的数字的平方和。然后重复这个过程直到这个数变为 1,也可能是 无限循环 但始终变不到 1。如果这个过程 结果为 1,…...

html css javascript写一个扫雷
文章目录 一、html css javascript写一个扫雷二、JavaScript三、HTML四、CSS五、热门文章 一、html css javascript写一个扫雷 一个简单的使用HTML、CSS和JavaScript编写的扫雷游戏的示例代码: <!DOCTYPE html> <html> <head><title>扫雷…...

Unity3D正则表达式的使用
系列文章目录 unity工具 文章目录 系列文章目录前言一、匹配正整数的使用方法1-1、代码如下1-2、结果如下 二、匹配大写字母2-1、代码如下1-2、结果如下 三、Regex类3-1、Match()3-2、Matches()3-3、IsMatch() 四、定义正则表达式…...

SpringBoot,TDengine时序数据库,实现物联网,车联网大批量数据更新最佳实践。
简介 TDengine 是一款专为物联网、工业互联网等场景设计并优化的大数据平台,它能安全高效地将大量设备、数据采集器每天产生的高达 TB 甚至 PB 级的数据进行汇聚、存储、分析和分发,对业务运行状态进行实时监测、预警,提供实时的商业洞察。其…...

【CSS】常见
一. 溢出隐藏 1.1 单行文本溢出 .content{max-width:200px; /* 定义容器最大宽度 */overflow:hidden; /* 隐藏溢出的内容 */text-overflow:ellipsis; /* 溢出部分...表示 */white-space: nowrap; /* 确保文本在一行内显示 */ }问题:display:flex 和 ellipsis 冲…...

Django实战
一、开发登录表单 def login_form(request):html <html><body><form method"post">用户名:<input name "username" type"text"></input></br>密码:<input name "password" type…...

redis(1)
文章目录 一、redis基础redis简介redis 特性单线程redis 对比 memcachedredis 典型应用场景 二、redis安装及连接yum安装redis查看yum仓库redis版本yum安装 redis 编译安装 redis编译安装前台启动redis启动多实例解决启动时的三个警告提示创建 redis 用户编辑 redis 服务器启动…...

【RTP】webrtc 学习2: webrtc对h264的rtp打包
切片只是拷贝帧的split的各个部分到新的rtp 包的封装中。并没有在rtp包本身标记是否为关键帧FU-A 切片 输入的H.264 数据进行split :SplitNalu SplitNalu : 按照最大1200字节进行切分 切分后会返回一个数组 对于FU-A :split的数据总大小是 去掉一个字节的nalu header size …...

【搜索术】代码阅读理解学习笔记
学习资料 《理解源代码》 1 静态阅读 1.1 目标结构 常见目录名含义docs项目文档examples示例代码...

vue 打包下载多层zip文件
npm install jszip npm install file-saverimport JSZip from "jszip"; import FileSaver from "file-saver"; import {Message} from "view-design";/*** 下载文件 传数组* fileList* [* {* type:file,* name:17034953113790548.png,…...

Flink实战五_状态机制
接上文:Flink实战四_TableAPI&SQL 在学习Flink的状态机制之前,我们需要理解什么是状态。回顾我们之前介绍的很多流计算的计算过程,有些计算方法,比如说我们之前多次使用的将stock.txt中的一行文本数据转换成Stock股票对象的ma…...

SQL中having与where的区别 简单明了
having子句与where都是设定条件筛选的语句,有相似之处也有区别。 having与where的区别: having是在分组后对数据进行过滤 where是在分组前对数据进行过滤 having后面可以使用聚合函数 where后面不可以使用聚合 在查询过程中执行顺序:from>where>g…...

Transformer 自然语言处理(二)
原文:Natural Language Processing with Transformers 译者:飞龙 协议:CC BY-NC-SA 4.0 第五章:文本生成 基于 Transformer 的语言模型最令人不安的特点之一是它们生成的文本几乎无法与人类写的文本区分开。一个著名的例子是 Ope…...

软件测试之软件缺陷管理
什么是软件缺陷 标准的定义:从产品内部看,缺陷是软件产品开发或维护过程中存在的错误、毛病等各种问题;从产品外部看,缺陷是系统所需要实现的某种功能的失效或违背 软件缺陷的生命周期 一个缺陷的正常生命周期是 新建ÿ…...