STM32基础教程——硬件SPI
目录
前言
SPI硬件电路
SPI部分特征
SPI框图
SPI数据收发过程
W25Q64
技术实现
接线图
代码实现
技术要点
引脚操作
SPI初始化
SPI起始信号
SPI终止信号
SPI字节交换
宏替换W25Q64操作指令
W25Q64写使能
忙等待
读取设备ID号和制造商ID
页写入
数据读取
实验结果
问题记录
前言
SPI(Serial Peripheral Interface,串行外设接口)是一种同步串行通信接口规范,主要用于短距离通信,广泛应用于嵌入式系统中。它使一个主设备能够与一个或多个从设备进行通信。SPI使用四条主要信号线:MOSI(主机输出/从机输入)、MISO(主机输入/从机输出)、SCK(串行时钟)和SS/CS(从选/片选)来实现数据的双向传输。这种接口方式支持全双工通信,具有传输速率高、延迟低的优点,但相比其他一些接口协议,使用的信号线较多。SPI常用于连接传感器、存储器、ADC(模数转换器)等外围设备。
SPI硬件电路
- 所有SPI设备的SCK,MISO,MOSI分别连在一起。
- 主机另外引出多条CS控制线分别连接到各从机的CS引脚。
- 输出引脚配置为推挽输出,输入引脚配置为浮空输入或上拉输入。
SPI部分特征
- 3线(SCK,MISO,MOSI)全双工同步传输
- 带或不带第三根双向数据线的双线单工同步传输(只使用一根MISO或MOSI进行双向半双工数据传输)
- 8或16位传输帧格式选择
- 8个主模式波特率预分频系数(最大为fPCLK/2)
- 可编程的时钟极性和相位
- 可编程的数据顺序,MSB在前或LSB在前
SPI框图
SPI数据收发过程
硬件SPI通常只使用非连续数据传输方式,时钟速率不需要太快。
(模式3)SCK被拉低后发送起始信号,要发送的数据被写入SPI_DR寄存器,TXE被清零,表示发送缓冲数据寄存器非空,然后数据从缓冲数据寄存器移入移位寄存器,TXE标志被置位,表示发送数据缓冲器空,因为使用非连续传输方式,暂时不用进行数据写入。然后数据从移位寄存器移出,一个时钟信号移出1位,同时从MISO移入数据接收缓冲寄存器一位,连续8个时钟信号后,一个字节的数据移出,一个字节的数据移入,完成一个字节数据的交换。读取SPI_SR寄存器的RXNE位判断接收数据寄存器内是否接收到数据。读出数据后方可进行下一个字节的写入与接收。
W25Q64
W25Q64是华邦公司(Winbond)推出的一款基于SPI通信的大容量闪存产品,其存储容量为64Mb(即8MB)。该芯片支持2.7~3.6V的工作电压范围,并且具备至少10万次的擦写周期和长达20年的数据保存时间。W25Q64以其灵活性和出色的性能著称,非常适合用于存储声音、文本和数据等应用。它被组织为32768个可编程页面,每个页面包含256字节的数据。此外,W25Q64还支持多种擦除命令,包括4KB扇区擦除、32KB块擦除、64KB块擦除以及全片擦除,并且兼容标准SPI模式0和模式3,最高支持133MHz的时钟频率。通过SPI接口,W25Q64可以轻松地与各种微控制器进行集成,实现高效的数据读写操作。
W25Q64由8MB的存储容量,存储区域被划分成了128个块,每个块又被划分成了16个扇区,每个扇区又被划分成了 16个页。
W25Q64写入操作时:
- 在写入操作之前,必须先进行写使能。
- 每个数据为只能有1改写成0,不能由0改写成1。
- 写入数据之前必须先擦除,擦除后,所有的数据位都变为1。
- 擦除必须以最小单元擦除(这里最小擦除单元时4kb扇区)。
- 连续写入多个字节时最多写入一页的数据(256Byte),超过页尾的数据会回到页首覆盖写入。
- 写入操作完成后,芯片进入忙状态,不响应新的写操作。
W25Q64读操作时:
- 直接调取读操作时序,无需写使能,无需其他操作,没有页限制,读取操作完成后芯片不会进入忙状态,但是芯片忙状态时不能进行读操作。
技术实现
原理图
接线图
代码实现
main.c
/**********************************************************
1.实验名称:硬件SPI读写W25Q64
2.实验环境:STM32F103C8T6最小系统板
3.实验内容:使用SPI外设实现读写W25Q64
4.作者;abai
5.实验时间:2025-5-7
**********************************************************/
#include "stm32f10x.h" // Device header
#include "Delay.h" //延时函数
#include "OLED.h"
#include "W25Q64.h"uint8_t MF;
uint16_t ID;
uint8_t SendArry[] = {0x05,0x06,0x07,0x08};
uint8_t ReceiveArry[4];int main(void)
{/*OLED初始化*/OLED_Init();W25Q64_Init();OLED_ShowString(1,1,"MF:");OLED_ShowString(2,1,"ID:");OLED_ShowString(3,1,"W:");OLED_ShowString(4,1,"R:");W25Q64_SectorErase(0x000000); //写入数据之前必须页擦除W25Q64_PageProgram(0x000000,SendArry,4);W25Q64_ReadData(0x000000,ReceiveArry,4);W25Q64_ReadID(&MF,&ID);OLED_ShowHexNum(3,3,SendArry[0],2);OLED_ShowHexNum(3,5,SendArry[1],2);OLED_ShowHexNum(3,7,SendArry[2],2);OLED_ShowHexNum(3,9,SendArry[3],2);OLED_ShowHexNum(4,3,ReceiveArry[0],2);OLED_ShowHexNum(4,5,ReceiveArry[1],2);OLED_ShowHexNum(4,7,ReceiveArry[2],2);OLED_ShowHexNum(4,9,ReceiveArry[3],2);OLED_ShowHexNum(1,4,MF,2);OLED_ShowHexNum(2,4,ID,4);while(1){}
}
MySPI.h
#ifndef MYSPI_H
#define MYSPI_H#include "stm32f10x.h" // Device headervoid MySPI_Init(void);
void MySPI_CS_W(uint8_t BitValue);
void MySPI_SCK_W(uint8_t BitValue);
void MySPI_MOSI_W(uint8_t BitValue);
uint8_t MySPI_MISO_R(void);
void MySPI_Start(void);
void MySPI_Stop(void);
uint8_t MySPI_SwapByte(uint8_t ByteValue);#endif
MySPI.c
#include "MySPI.h"/***@brief SPI初始化*@param None*@retval None**/
void MySPI_Init(void)
{RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1,ENABLE);//GPIOGPIO_InitTypeDef GPIO_InitStruct;GPIO_InitStruct.GPIO_Pin = GPIO_Pin_4;GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA,&GPIO_InitStruct);GPIO_InitStruct.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_7;GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA,&GPIO_InitStruct);GPIO_InitStruct.GPIO_Pin = GPIO_Pin_6;GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU; //上拉输入GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA,&GPIO_InitStruct);//SPISPI_InitTypeDef SPI_InitStruct;SPI_InitStruct.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_128; //SPI时钟采用32分频SPI_InitStruct.SPI_CPHA = SPI_CPHA_1Edge; //选择第几个上升沿捕获数据SPI_InitStruct.SPI_CPOL = SPI_CPOL_Low; //时钟默认为低电平SPI_InitStruct.SPI_CRCPolynomial = 7; //CRC校验,这里不使用给一个默认值7SPI_InitStruct.SPI_DataSize = SPI_DataSize_8b; //数据字节长度为8SPI_InitStruct.SPI_Direction = SPI_Direction_2Lines_FullDuplex; //数据传输采用双向全双工SPI_InitStruct.SPI_FirstBit = SPI_FirstBit_MSB; //数据传输采用高位在前SPI_InitStruct.SPI_Mode = SPI_Mode_Master; //SPI主模式SPI_InitStruct.SPI_NSS = SPI_NSS_Soft; //使用GPIO模拟CS,这里设置为软件控制即可SPI_Init(SPI1,&SPI_InitStruct);//使能SPISPI_Cmd(SPI1,ENABLE);//默认CS为高电平MySPI_CS_W(1);
}/***@brief SPI CS控制*@param BitValue 要写入CS的位数据*@retval None**/
void MySPI_CS_W(uint8_t BitValue)
{GPIO_WriteBit(GPIOA,GPIO_Pin_4,(BitAction)BitValue);
}/***@brief SPI起始信号*@param None*@retval None**/
void MySPI_Start(void)
{MySPI_CS_W(0); //将片选信号拉低发送起始信号
}/***@brief SPI终止信号*@param None*@retval None**/
void MySPI_Stop(void)
{MySPI_CS_W(1); //将片选信号拉高发送终止信号
}/***@brief SPI交换字节*@param None*@retval 交换的字节**/
uint8_t MySPI_SwapByte(uint8_t ByteValue)
{while(!SPI_I2S_GetFlagStatus(SPI1,SPI_I2S_FLAG_TXE));SPI_I2S_SendData(SPI1,ByteValue);while(!SPI_I2S_GetFlagStatus(SPI1,SPI_I2S_FLAG_RXNE));return SPI_I2S_ReceiveData(SPI1);
}
W25Q64_Ins.h
#ifndef W25Q64_INS_H
#define W25Q64_INS_H#define W25Q64_WRITEENABLE 0x06
#define W25Q64_READSTATUSREGISTER1 0x05
#define W25Q64_PAGEPROGRAM 0x02
#define W25Q64_SECTORERASE4KB 0x20
#define W25Q64_JEDECID 0X9F
#define W25Q64_READDATA 0x03
#define W25Q64_DUMMY_BYTE 0xFF#endif
W25Q64.h
#ifndef W25Q64_H
#define W25Q64_H#include "stm32f10x.h" // Device header
#include "MySPI.h"void W25Q64_Init(void);
void W25Q64_ReadID(uint8_t* MF, uint16_t*ID);
void W25Q64_WriteEnable(void);
void W25Q64_WaitBusy(void);
void W25Q64_PageProgram(uint32_t Address, uint8_t* DataArry, uint16_t Count);
void W25Q64_SectorErase(uint32_t Address);
void W25Q64_ReadData(uint32_t Address,uint8_t* DataArry, uint32_t Count);#endif
W25Q64.c
#include "W25Q64.h"
#include "W25Q64_Ins.h"/***@brief W25Q64初始化*@param None*@retval None**/
void W25Q64_Init(void)
{MySPI_Init();
}/***@brief W25Q64ID读取*@param None*@retval None**/
void W25Q64_ReadID(uint8_t* MF, uint16_t*ID)
{MySPI_Start();//发送读取ID指令MySPI_SwapByte(W25Q64_JEDECID);//发送空指令交换数据*MF = MySPI_SwapByte(W25Q64_DUMMY_BYTE); //随便发送一个数据用于与从机交换数据,一般使用0xFF或0x00*ID = MySPI_SwapByte(W25Q64_DUMMY_BYTE);*ID <<= 8;*ID |= MySPI_SwapByte(W25Q64_DUMMY_BYTE);MySPI_Stop();
}/***@brief W25Q64ID写使能*@param None*@retval None**/
void W25Q64_WriteEnable(void)
{MySPI_Start();//发送写使能指令MySPI_SwapByte(W25Q64_WRITEENABLE);MySPI_Stop();
}/***@brief W25Q64ID忙等待*@param None*@retval None**/
void W25Q64_WaitBusy(void)
{MySPI_Start();//发送读状态寄存器指令MySPI_SwapByte(W25Q64_READSTATUSREGISTER1);//等待BUSY位清零,则从设备退出忙状态while((MySPI_SwapByte(W25Q64_DUMMY_BYTE) & 0X01) == 0X01);MySPI_Stop();
}/***@brief W25Q64ID页写入*@param None*@retval None**/
void W25Q64_PageProgram(uint32_t Address, uint8_t* DataArry, uint16_t Count)
{uint16_t i;W25Q64_WriteEnable(); //写使能MySPI_Start();MySPI_SwapByte(W25Q64_PAGEPROGRAM);//发送24位地址MySPI_SwapByte(Address>>16);MySPI_SwapByte(Address>>8);MySPI_SwapByte(Address);//发送指定数量的数据for(i=0;i<Count;i++){MySPI_SwapByte(DataArry[i]);}MySPI_Stop();W25Q64_WaitBusy(); //事后等待忙状态
}/***@brief W25Q64ID指定扇区擦除*@param None*@retval None**/
void W25Q64_SectorErase(uint32_t Address)
{ W25Q64_WriteEnable();MySPI_Start();MySPI_SwapByte(W25Q64_SECTORERASE4KB);//发送24位地址MySPI_SwapByte(Address>>16);MySPI_SwapByte(Address>>8);MySPI_SwapByte(Address);MySPI_Stop();W25Q64_WaitBusy();
}/***@brief W25Q64ID读取数据*@param None*@retval None*@note 数据接收的数量也要指定,不然无法停止数据读取操作**/
void W25Q64_ReadData(uint32_t Address,uint8_t* DataArry, uint32_t Count)
{uint32_t i;MySPI_Start();MySPI_SwapByte(W25Q64_READDATA);//发送24位地址MySPI_SwapByte(Address>>16);MySPI_SwapByte(Address>>8);MySPI_SwapByte(Address);for(i=0;i<Count;i++){DataArry[i] = MySPI_SwapByte(W25Q64_DUMMY_BYTE);}MySPI_Stop();
}
OLED部分代码参照文章《STM32基础教程——OLED显示》
技术要点
引脚操作
/***@brief SPI CS控制*@param BitValue 要写入CS的位数据*@retval None**/
void MySPI_CS_W(uint8_t BitValue)
{GPIO_WriteBit(GPIOA,GPIO_Pin_4,(BitAction)BitValue);
}
对单个引脚的操作进行封装,用于模拟SPI引脚电平翻转用于控制CS信号。
SPI初始化
/***@brief SPI初始化*@param None*@retval None**/
void MySPI_Init(void)
{RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1,ENABLE);//GPIOGPIO_InitTypeDef GPIO_InitStruct;GPIO_InitStruct.GPIO_Pin = GPIO_Pin_4;GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA,&GPIO_InitStruct);GPIO_InitStruct.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_7;GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA,&GPIO_InitStruct);GPIO_InitStruct.GPIO_Pin = GPIO_Pin_6;GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU; //上拉输入GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA,&GPIO_InitStruct);//SPISPI_InitTypeDef SPI_InitStruct;SPI_InitStruct.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_128; //SPI时钟采用128分频SPI_InitStruct.SPI_CPHA = SPI_CPHA_1Edge; //选择第几个上升沿捕获数据SPI_InitStruct.SPI_CPOL = SPI_CPOL_Low; //时钟默认为低电平SPI_InitStruct.SPI_CRCPolynomial = 7; //CRC校验,这里不使用给一个默认值7SPI_InitStruct.SPI_DataSize = SPI_DataSize_8b; //数据字节长度为8SPI_InitStruct.SPI_Direction = SPI_Direction_2Lines_FullDuplex; //数据传输采用双向全双工SPI_InitStruct.SPI_FirstBit = SPI_FirstBit_MSB; //数据传输采用高位在前SPI_InitStruct.SPI_Mode = SPI_Mode_Master; //SPI主模式SPI_InitStruct.SPI_NSS = SPI_NSS_Soft; //使用GPIO模拟CS,这里设置为软件控制即可SPI_Init(SPI1,&SPI_InitStruct);//使能SPISPI_Cmd(SPI1,ENABLE);//默认CS为高电平MySPI_CS_W(1);
}
SPI初始化,先初始化GPIO,PA4作为软件发送CS信号的引脚,应初始化为推挽输出。PA5和PA7作为外设SPI的SCK和MOSI引脚,应初始化为复用推挽输出,PA6引脚作为MISO引脚,用于接收数据输入,初始化为上拉输入。
SPI1初始化,时钟分频选择128分频,SPI1为APB2外设,系统时钟为72MHz,经分频后时钟频率为562.5kHz。设置第一个边沿数据移入,且默认时钟为低电平。将SPI设置为主模式下双向全双工模式,数据长度设置为8位,数据传输高位在前。其余杂项CRC校验设为默认值,NSS在本实验不使用,设置为软件控制。将片选信号初始化为高电平。
SPI起始信号
SPI的起始时序是CS(SS)从高电平切换为低电平 。
/***@brief SPI起始信号*@param None*@retval None**/
void MySPI_Start(void)
{MySPI_CS_W(0); //将片选信号拉低发送起始信号
}
SPI终止信号
SPI的终止时序是CS(SS)从低电平切换为高电平 。
/***@brief SPI终止信号*@param None*@retval None**/
void MySPI_Stop(void)
{MySPI_CS_W(1); //将片选信号拉高发送终止信号
}
SPI字节交换
SPI为同步全双工通信,每一次通信都会进行数据交换。
/***@brief SPI交换字节*@param None*@retval 交换的字节**/
uint8_t MySPI_SwapByte(uint8_t ByteValue)
{while(!SPI_I2S_GetFlagStatus(SPI1,SPI_I2S_FLAG_TXE));SPI_I2S_SendData(SPI1,ByteValue);while(!SPI_I2S_GetFlagStatus(SPI1,SPI_I2S_FLAG_RXNE));return SPI_I2S_ReceiveData(SPI1);
}
硬件SPI交换字节数据,先等待TXE标志位置位,表示发送数据缓冲寄存器为空,方可向发送数据缓冲寄存器写入数据,硬件SPI会在数据写入发送数据寄存器后自动完成数据发送,无需手动操作。然后只需等待数据接收寄存器的标志位RXNE被置位即可,当RXNE被置位后,说明数据接收寄存器已接收到从机发来的数据,此时将数据读出即可。
宏替换W25Q64操作指令
#ifndef W25Q64_INS_H
#define W25Q64_INS_H#define W25Q64_WRITEENABLE 0x06
#define W25Q64_READSTATUSREGISTER1 0x05
#define W25Q64_PAGEPROGRAM 0x02
#define W25Q64_SECTORERASE4KB 0x20
#define W25Q64_JEDECID 0X9F
#define W25Q64_READDATA 0x03
#define W25Q64_DUMMY_BYTE 0xFF#endif
将对W25Q64操作的命令使用宏代替, 提高代码的便捷性和可读性。
W25Q64写使能
/***@brief W25Q64ID写使能*@param None*@retval None**/
void W25Q64_WriteEnable(void)
{MySPI_Start();//发送写使能指令MySPI_SwapByte(W25Q64_WRITEENABLE);MySPI_Stop();
}
将写使能操作封装,方便使用。主机与从机交换数据,发送写使能命令,对从机进行写使能操作。
忙等待
/***@brief W25Q64ID忙等待*@param None*@retval None**/
void W25Q64_WaitBusy(void)
{MySPI_Start();//发送读状态寄存器指令MySPI_SwapByte(W25Q64_READSTATUSREGISTER1);//等待BUSY位清零,则从设备退出忙状态while((MySPI_SwapByte(W25Q64_DUMMY_BYTE) & 0X01) == 0X01);MySPI_Stop();
}
发送读取状态寄存器1的命令,判断BUSY位的状态,使用while循环等待芯片退出忙状态将BUSY位清零。
读取设备ID号和制造商ID
/***@brief W25Q64ID读取*@param None*@retval None**/
void W25Q64_ReadID(uint8_t* MF, uint16_t*ID)
{MySPI_Start();//发送读取ID指令MySPI_SwapByte(W25Q64_JEDECID);//发送空指令交换数据*MF = MySPI_SwapByte(W25Q64_DUMMY_BYTE); //随便发送一个数据用于与从机交换数据,一般使用0xFF或0x00*ID = MySPI_SwapByte(W25Q64_DUMMY_BYTE);*ID <<= 8;*ID |= MySPI_SwapByte(W25Q64_DUMMY_BYTE);MySPI_Stop();
}
主机发送读取JEDEC ID的命令,从机向主机发送数据,数据格式为第一个字节为制造商ID,第二个和第三个字节为设备ID。这里使用指针进行数据操作。
页写入
/***@brief W25Q64ID页写入*@param None*@retval None**/
void W25Q64_PageProgram(uint32_t Address, uint8_t* DataArry, uint16_t Count)
{uint16_t i;W25Q64_WriteEnable(); //写使能MySPI_Start();MySPI_SwapByte(W25Q64_PAGEPROGRAM);//发送24位地址MySPI_SwapByte(Address>>16);MySPI_SwapByte(Address>>8);MySPI_SwapByte(Address);//发送指定数量的数据for(i=0;i<Count;i++){MySPI_SwapByte(DataArry[i]);}MySPI_Stop();W25Q64_WaitBusy(); //事后等待忙状态
}
页写入操作,先进行写使能,然后发送起始信号,发送页写入命令。然后发送24位地址,高16位为页地址,低八位为字节地址。使用for循环写入指定数量的数据,数据的数量不应超过256字节。最后发送停止信号,然后进行芯片忙等待操作(忙等待如果加在函数开始,那读取操作也要加忙等待函数调用,这样会使操作更复杂。)。所有写操作函数与本函数类似,不再赘述。
数据读取
/***@brief W25Q64ID读取数据*@param None*@retval None*@note 数据接收的数量也要指定,不然无法停止数据读取操作**/
void W25Q64_ReadData(uint32_t Address,uint8_t* DataArry, uint32_t Count)
{uint32_t i;MySPI_Start();MySPI_SwapByte(W25Q64_READDATA);//发送24位地址MySPI_SwapByte(Address>>16);MySPI_SwapByte(Address>>8);MySPI_SwapByte(Address);for(i=0;i<Count;i++){DataArry[i] = MySPI_SwapByte(W25Q64_DUMMY_BYTE);}MySPI_Stop();
}
数据读取操作无需写使能,无需进行其他操作。发送读数据命令,然后发送24位地址,同样高16位地址为页地址,低八位为字节地址。读取操作不受页限制。
实验结果
问题记录
暂无
相关文章:
STM32基础教程——硬件SPI
目录 前言 SPI硬件电路 SPI部分特征 SPI框图 SPI数据收发过程 W25Q64 技术实现 接线图 代码实现 技术要点 引脚操作 SPI初始化 SPI起始信号 SPI终止信号 SPI字节交换 宏替换W25Q64操作指令 W25Q64写使能 忙等待 读取设备ID号和制造商ID 页写入 数…...
系统架构-云原生架构设计
内涵 基于云原生技术,旨在将云应用中的非业务代码部分进行最大化的剥离,让云设施接管应用中原有的大量非功能特性。 云原生的代码包括三部分:业务代码、三方软件、处理非功能特性的代码 具备云原生架构的应用可以最大程度利用云服务和提升…...
ROS2: 服务通信
目录 服务通信模型服务通信的C实现服务端客户端 关键函数说明 服务通信模型 服务通信模型如上图所示,分为服务端和客户端,客户端根据需要向服务端发送请求(Request),服务端处理请求,并向客户端发回响应&…...
贵州省棒球运动发展中长期规划(2024-2035)·棒球1号位
贵州省棒球运动发展中长期规划(2024-2035) Guizhou Province Baseball Development Medium & Long-Term Plan (2024-2035) 一、战略定位 | Strategic Positioning 立足贵州山地特色与民族文化,借鉴洛杉矶"社区棒球"模式&…...
深度学习中的autograd与jacobian
1. autograd 对于一个很简单的例子,如下图所示,对于一个神经元z,接收数据x作为输入,经过激活函数,获得激活后的结果,最后利用损失函数获得损失,然后梯度反向回传。 上图右侧即梯度反向回传的过…...
Ubuntu 使用dotfiles个性化配置模板
dotfiles 什么是dotfilercm软件手动修改/生成dotfile启动脚本 .bash_profile按键绑定 .inputrc别名 .alias其他dotfiles 从github克隆从Github库中下载代码让dotfile文件生效 GUN stow管理初始化目录结构使用Stow 参考文章 什么是dotfile 每个人都有自己用电脑的习惯ÿ…...
VIVADO IP核整理(二)——FFT
目录 IP 核配置IP 核接口s_axis_config_tdata 配置输入输出端口描述 仿真 参考:FFT IP核 详细介绍 参考:官方文档介绍 IP 核配置 在 IP Catalog 中搜索:Fast Fourier Transform 按照上图所示进行配置,下文对配置内容进行详述。 …...
Excel处理控件Aspose.Cells教程:压缩Excel文件完整指南
Excel 电子表格是管理、分析和可视化数据的有效工具,但随着文件复杂度的增加,它们很快就会变得臃肿。无论是由于数据集庞大、嵌入图片、格式过多还是隐藏工作表,Excel 文件的大小都可能迅速膨胀,导致打开速度变慢、难以通过电子邮…...
AKS 网络深入探究:Kubenet、Azure-CNI 和 Azure-CNI(overlay)
Kubernetes 网络使您能够配置 Kubernetes 网络内的通信。部署 AKS 集群时,有三种网络模型需要考虑: Kubenet 网络 KubeNet 是 AKS 中的基础网络插件。它可以被形象地比喻成大城市的地铁系统。地铁可能无法直接连接所有可能的地点(例如您的服…...
angular的cdk组件库
目录 一、虚拟滚动 一、虚拟滚动 <!-- itemSize相当于每个项目的高度为30px --><!-- 需要给虚拟滚动设置宽高,否则无法正常显示 --> <cdk-virtual-scroll-viewport [itemSize]"40" class"view_scroll"><div class"m…...
unity 使用蓝牙通讯(PC版,非安卓)
BlueTooth in pc with unity 最近接到的需求是在unity里面开发蓝牙功能,其实一开始我并不慌,因为据我所知,unity有丰富的插件可以使用,但是问题随之而来 1.unity里面无法直接与蓝牙通讯(后来找到了开启runtime一类的东西,但是我找了半天也没找到在哪里可以打开) 2.引入dll通过d…...
Feign 重试策略调整:优化微服务通信的稳定性
在微服务架构中,服务之间的通信是常见的场景。然而,网络问题、服务不稳定或临时故障都可能导致通信失败。Feign 是一个流行的声明式 REST 客户端,广泛用于微服务间的通信。通过合理调整 Feign 的重试策略,可以显著提高系统的稳定性…...
Nacos源码—5.Nacos配置中心实现分析一
大纲 1.关于Nacos配置中心的几个问题 2.Nacos如何整合SpringBoot读取远程配置 3.Nacos加载读取远程配置数据的源码分析 4.客户端如何感知远程配置数据的变更 5.集群架构下节点间如何同步配置数据 1.关于Nacos配置中心的几个问题 问题一:SpringBoot项目启动时如…...
【spring】Spring、Spring MVC、Spring Boot、Spring Cloud?
这些都是 Spring 家族的重要组成部分,但它们各自定位不同、功能层级不同,可以用一张表格和简要说明来帮你快速理解: 一、四者概念和区别表格 名称功能定位主要用途/核心功能是否依赖其他部分Spring基础框架(核心)IOC、…...
RDD的处理过程
1. 创建RDD 通过SparkContext的parallelize方法从本地集合创建RDD。 从外部存储(如HDFS、本地文件系统)加载数据创建RDD。 通过对已有RDD进行转换操作生成新的RDD。 2. 转换操作(Transformation) 对RDD进行操作(如…...
Vue3 中当组件嵌套层级较深导致 ref 无法直接获取子组件实例时,可以通过 provide/inject + 回调函数的方式实现子组件方法传递到父组件
需求:vue3中使用defineExposeref调用子组件方法报错不是一个function 思路:由于组件嵌套层级太深导致ref失效,通过provide/inject 回调函数来实现多层穿透 1. 父组件提供「方法注册函数」 父组件通过 provide 提供一个用于接收子组件方法…...
如何在Ubuntu上安装NVIDIA显卡驱动?
作者:算力魔方创始人/英特尔创新大使刘力 一,前言 对于使用NVIDIA显卡的Ubuntu用户来说,正确安装显卡驱动是获得最佳图形性能的关键。与Windows系统不同,Linux系统通常不会自动安装专有显卡驱动。本文将详细介绍在Ubuntu系统上安…...
Linux 修改bond后网关不生效的问题
1.前言 bond原本是OK的,但是某个同事变更后,发现网关路由存在问题 #查看路由,默认网关信息,发现没有配置的网关信息 ip route show #排查/etc/sysconfig/network-script/下面的ifcfg-* 文件没有问题 1.重启network 服务 systemct…...
chili调试笔记13 工程图模块 mesh渲染 mesh共享边显示实现
把模型投影到工程图要用什么模块当工程图的画板,最后要导出dxf的 three是怎么读取他的3d数据的 mesh不是三角形吗怎么渲染出四边形面的 我想看到三角形的边怎么设置 ai让我干嘛我就干嘛 static getAllEdges(face: { positions: Float32Array; indices: Uint16Array …...
Eclipse 插件开发 5 编辑器
Eclipse 插件开发 5 编辑器 1 编辑器描述2 自定义编辑器2.1 注册插件(plugin.xml)2.2 继承 EditorPart2.3 实现 IEditorInput2.4 打开编辑器 1 编辑器描述 Eclipse 的 UI 基于 Workbench(工作台)模型,Editor Area 是 Workbench 的核心区域之…...
Java消息队列性能优化实践:从理论到实战
Java消息队列性能优化实践:从理论到实战 1. 引言 在现代分布式系统架构中,消息队列(Message Queue,MQ)已经成为不可或缺的中间件组件。它不仅能够实现系统间的解耦,还能提供异步通信、流量削峰等重要功能…...
Android学习总结之Binder篇
一、Binder 跨进程通信底层实现 Q1:Binder 如何实现一次完整的跨进程方法调用?请描述内核态与用户态交互流程 高频错误:仅回答 “通过 AIDL 生成代码”,未涉及 Binder 驱动三层协作模型 满分答案(附内核交互流程图&a…...
very_easy_sql(SSRF+SQL注入)
题目有一行提示: you are not an inner user, so we can not let you have identify~(你不是内部用户,所以我们不能让你进行身份验证)联想到可能存在SSRF漏洞,一般情况下,SSRF攻击的目标是外网无法访问的内…...
MCP认证全解析:从零到微软认证专家
MCP认证全解析:从零到微软认证专家 什么是MCP认证? Microsoft Certified Professional(MCP)是由微软官方颁发的技术认证,旨在验证IT从业者在微软技术栈(如Azure、Windows Server、SQL Server等࿰…...
leetcode刷题日记——反转链表
[ 题目描述 ]: [ 思路 ]: 题目要求将链表中指定位置的部分进行翻转反转的部分,其实可以看做使用头插法插入链表运行如下: struct ListNode* reverseBetween(struct ListNode* head, int left, int right) {struct ListNode te…...
【day04】Fibonacci数列 | 单词搜索 | 杨辉三角
1.Fibonacci数列 题目链接: Fibonacci数列_牛客题霸_牛客网 解题思路: 求斐波那契数列的过程中,判断⼀下:何时n会在两个fib数之间。 #include <bits/stdc.h>using namespace std;#define int long longsigned main() {i…...
win11指定Microsoft Print To PDF的输出路径(电子书djvu转pdf输出路径)
首先,看一张效果图 前面都是废话,解决方法看最后,看最后 Microsoft Print To PDF功能作为Windows操作系统内置的一项便捷工具,为用户提供了将任何可打印内容高效转换为PDF格式的能力。本文深入探讨了Microsoft Print To PDF的工作…...
第3章 数据和C
目录 3.1 示例程序3.2 变量与常量数据3.3 数据:数据类型关键字3.4 C数据类型3.4.1 int类型3.4.2 其它整数类型3.4.3 使用字符:char类型3.4.4 _Bool类型3.4.5 可移植的类型:inttypes.h3.4.6 float、double和long double类型3.4.7 复数和虚数类…...
迁移学习:如何加速模型训练和提高性能
📌 友情提示: 本文内容由银河易创AI(https://ai.eaigx.com)创作平台的gpt-4-turbo模型生成,旨在提供技术参考与灵感启发。文中观点或代码示例需结合实际情况验证,建议读者通过官方文档或实践进一步确认其准…...
Kotlin zip 函数的作用和使用场景
1. zip 函数的作用 zip 是 Kotlin 集合操作中的一个函数,用于将两个集合按照索引一一配对,生成一个新的 List<Pair<T, R>> 集合。 作用:将两个集合的元素按位置组合成键值对(Pair)。返回值:一…...
通用分布式锁组件
Redisson的分布式锁使用并不复杂,基本步骤包括: 1)创建锁对象 2)尝试获取锁 3)处理业务 4)释放锁 但是,除了第3步以外,其它都是非业务代码,对业务的侵入较多&#x…...
FastDFS,分布式文件存储系统,介绍+配置+工具类
FastDFS 什么是分布式文件存储系统 随着文件逐渐增多,单台计算机已经存储不下这么多数据,需要用多台计算机存储不同的数据或进行备份,这样就需要有一个管理系统管理不同的计算机节点,这就是分布式管理系统。 使用分布式管理系统的…...
查询nvidia边缘设备的软硬件版本jetson_release
通过jetson_release命令可查询nvidia边缘设备的软硬件版本 nvidianvidia-desktop:~/leo/test_onnx$ jetson_release Software part of jetson-stats 4.2.12 - (c) 2024, Raffaello Bonghi Model: NVIDIA Orin Nano Developer Kit - Jetpack 5.1.1 [L4T 35.3.1] NV Power Mode[…...
[学习]RTKLib详解:ppp.c与ppp_ar.c
文章目录 RTKLib详解:ppp.c与ppp_ar.cPart A: ppp.c一、整体作用与工作流程二、核心函数说明1. pppos2. res_ppp3. tide_solid4. prectrop5. corrmeas6. udbias_ppp 三、数学原理补充四、代码特点 Part B: ppp_ar.c一、整体作用与工作流程分析二、函数功…...
ntdll!LdrpSnapThunk函数分析之LdrpNameToOrdinal函数返回之后得到函数地址
第一部分: OrdinalNumber LdrpNameToOrdinal( ImportString, ExportDirectory->NumberOfNames, DllBase, NameTableBase, …...
LeetCode 267:回文排列 II —— Swift 解法全解析
文章目录 摘要描述题解答案题解代码分析统计字符频率判断是否可能构成回文构建半边字符数组回溯生成半边排列 示例测试及结果时间复杂度空间复杂度实际使用场景:回文排列在真实项目里能干啥?文本处理、数据清洗类系统游戏开发:名字合法性验证…...
【渗透测试】命令执行漏洞的原理、利用方式、防范措施
文章目录 命令执行漏洞的原理、利用方式、防范措施一、原理**1. 定义与触发条件****2. 攻击链流程图** 二、利用方式**1. 利用手法与分类**(1) 系统命令注入(2) 代码执行漏洞(3) 框架漏洞利用 **2. 案例** 三、防范措施**1. 输入过滤与验证****2. 禁用危险函数****3. 安全开发*…...
旧版谷歌浏览器Chrome v116.0.5845.141下载
63位和32位均有: https://bbs.pcbeta.com/forum.php?modviewthread&tid1978299 https://www.31du.cn/open/google-chrome-v116-0-5845-141.html v116.0.5845.141的win32位版本: https://www.cr173.com/soft/435106.html v116.0.5845.97版本&…...
行业洞察| 当大模型开始协同工作:多智能体系统的崛起与挑战
你有没有想过,如果一群AI智能体拉了个工作群,它们会聊些什么? 程序员AI:“这段代码我来写!” 产品经理AI:“需求还没说完呢!” 辩论家AI:“我觉得这个方案不行!” 吃瓜…...
The Action Replay Process
Preface A commonly used inequality − x > ln ( 1 − x ) , 0 < x < 1 -x > \ln(1 - x), \quad 0 < x < 1 −x>ln(1−x),0<x<1 Proof: Let f ( x ) ln ( 1 − x ) x f(x) \ln(1 - x) x f(x)ln(1−x)x, for 0 < x < 1 0 < …...
Python基于Django的病人信息管理系统及安全策略分析(附源码,文档说明)
博主介绍:✌IT徐师兄、7年大厂程序员经历。全网粉丝15W、csdn博客专家、掘金/华为云//InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ 🍅文末获取源码联系🍅 👇🏻 精彩专栏推荐订阅👇dz…...
YOLOv1:开创实时目标检测新纪元
一、引言 在计算机视觉领域,目标检测一直是重要的研究方向,广泛应用于自动驾驶、安防监控、智能机器人等场景。2016年,Joseph Redmon等人提出的YOLO(You Only Look Once)v1模型,以其端到端、单阶段、实时性…...
【今日三题】跳台阶扩展问题(找规律) / 包含不超过两种字符的最长子串 / 字符串的排列(回溯—全排列)
⭐️个人主页:小羊 ⭐️所属专栏:Linux 很荣幸您能阅读我的文章,诚请评论指点,欢迎欢迎 ~ 目录 跳台阶扩展问题(找规律)包含不超过两种字符的最长子串(字符串哈希)字符串的排列(回溯—全排列) 跳台阶扩展问题(找规律) 跳台阶扩…...
MySQL 中常见的日志
1. MySQL 中常见的日志有哪些? MySQL 主要包含以下几种日志: 错误日志(Error Log):记录 MySQL 服务器的启动和停止过程中的信息,以及运行过程中出现的错误或警告信息。默认情况下,错误日志文件…...
ubuntu nobel + qt5.15.2 设置qss语法识别正确
问题展示 解决步骤 首选项里面的高亮怎么编辑选择都没用。如果已经有generic-highlighter和css.xml,直接修改css.xml文件最直接! 在generic-highlighter目录下找到css.xml文件,位置是:/opt/Qt/Tools/QtCreator/share/qtcreator/…...
线程池技术
线程池基本概念 线程池就是在任务还没有到来前,预先创建一定数量的线程放入空闲列表。这些线程都是处于阻塞状态,不消耗CPU,但占用较小的内存空间。 当新任务到来时,缓冲池选择一个空线程,把任务传入此线程中运行&…...
matlab App自动化安装和卸载
这个是文件mlappinstall安装和卸载的使用函数,并且包括运行函数。File must be a toolbox file (*.mlappinstall) Package and Share Apps — Functions matlab.apputil.createCreate or modify app project file interactively using the Package App dialog box…...
MATLAB技巧——命令行输入的绘图,中文是正常的,到了脚本(m文件)里面就变成乱码的解决方法
文章目录 文件编码(根本性措施)字体设置使用 sprintf 或 text 函数系统语言设置示例代码 使用mlx方法 总结 在 M A T L A B MATLAB MATLAB中,中文字符在命令行和脚本中的显示问题通常与字符编码设置有关。以下是一些可能导致中文乱码的原因及…...
使用OpenCV 和 Dlib 实现人脸融合技术
文章目录 引言一、技术概述二、环境准备三、关键代码解析1. 人脸关键点定义2. 获取人脸掩模3. 计算仿射变换矩阵4. 检测并提取人脸关键点5. 颜色校正 四、完整流程五、效果展示六、总结 引言 本文将介绍如何使用Python、OpenCV和dlib库实现人脸融合技术,将一张人脸…...
Codeforces Round 1022 (Div. 2)
Problem - A - Codeforces 看这数据量,算出每个排列来,是不现实的,需要找找规律 来看找规律代码 #include <bits/stdc.h> using namespace std;int main() {int t;cin >> t;while (t--){int n;cin >> n;vector<int&g…...