当前位置: 首页 > article >正文

STM32中的SPI通信协议

IIC和SPI的对比

  1. IIC是半双工的通信,无法同时收发信息;SPI是全双工通讯,可以同时收发信息;
  2. IIC的通讯协议较复杂,而SPI通讯协议较简单;
  3. IIC需要通过地址选择从机,而SPI只主要一个引脚即可选中从机;
  4. IIC的通讯速率一般是100kH左右,而SPI可达50MHz;
  5. IIC的通讯线较少,而SPI需要较多。

SPI的介绍

什么是SPI ?

        SPI是串行外设接口(Serial Peripheral Interface的缩写,是一种高速 全双工 同步的通信总线,并且在芯片上只有四个引脚,同时PCB布线上节省空间,比较方便,正是出于这种简单易用的特性,越来越多的芯片集成了这种通信协议,比如 AT91RM9200

SPI的物理架构

SPI总线包含4个通讯线,分别是 SS , MISO , MOSI , SCL 。他们的 作用 如下:

  • MISO – Master Input Slave Output,主设备数据输入,从设备数据输出
  • MOSI – Master Output Slave Input,主设备数据输出,从设备数据输入
  • SCK – Serial Clock,时钟信号,由主设备产生
  • CS – Chip Select片选信号,由主设备控制

STM32F1 系列芯片有 3 SPI 接口。

SPI工作原理

  • 上述中是高位先行
  • 习惯上利用0XFF将从机上的数据交换出来。 
  • SPI通信只有主模式和从模式,没有明确的读和写操作之分。实际上,外设的写操作和读操作是同步完成的。在SPI通信中,发送一个数据必然会收到一个数据;如果要接收一个数据,就必须先发送一个数据。

  • 如果只进行写操作,主机可以忽略从设备传输过来的字节,因为主机不需要接收数据。

  • 如果主机要读取从设备的一个字节,那么主机必须发送一个空字节来引发从设备的传输。

说明:(狸猫换太子) 

        上述中主机和从机 高位先行,主机移位寄存器中,高位移出,其余整体向左移一位;同时,从机中的高位移到主机的低位中的空位,从机其余位向左移一位;主机的高位放到从机移位寄存器中空出来的低位。通过波特率发生器:一个时钟信号主从机中数据交换一位,8个时钟信号后,数据全部交换完成。

具体流程:

SPI 通信中,主机和从机都有一个串行移位寄存器。主机通过向自己的 SPI 串行寄存器写入一个字节来发起传输。

  1. 首先,拉低相应的 SS 信号线,表示与特定的从机进行通信。
  2. 主机通过发送 SCLK 时钟信号告诉从机进行数据的读写操作。
  3. 注意,SCLK 时钟信号可以是低电平有效或高电平有效,因为SPI有不同的模式(下文将介绍)。
  4. 主机将要发送的数据写入发送数据缓冲区,然后通过移位寄存器逐位地将数据传输给从机的串行移位寄存器,使用 MOSI 信号线进行传输。同时,从机的 MOSI 接口接收到的数据也经过移位寄存器一位一位地移到接收缓冲区。
  5. 从机也通过 MISO 信号线将自己串行移位寄存器中的内容返回给主机。同时,从机通过MOSI 信号线接收主机发送的数据。这样,两个移位寄存器中的内容就被交换。
  • 框图(参考手册): 

  •  简图:

SPI工作模式 

时钟极性(CPOL)

没有数据传输时时钟线SCL的空闲状态电平

0:SCK在空闲状态保持低电平

1:SCK在空闲状态保持高电平

时钟相位(CPHA)

 时钟线(SCK)在第几个时钟边沿采样数据

0:SCK在第一个(奇数)边沿进行数据位采样,数据在第一个时钟边沿被锁存;

1:SCK的第二个(偶数)边沿进行数据位采样,数据在第二个时钟边沿被锁村。

SPI的工作模式(开始和采样)
SPI 工作模式
CPOL
CPHA
SCL 空闲状态
采样边沿
采样时刻
000低电平上升沿奇数边沿
101低电平下降沿偶数边沿
210高电平下降沿奇数边沿
311高电平上升沿偶数边沿

 注:红色字体是常用的:模式0和模式3

 时序图

  • 模式0时序图:

  • 模式3时序图 :

STM32F103板上SPI的引脚

  • SPI1引脚: 

  •  SPI2引脚:

  •  SP3引脚 
  • STM32F1系列根据Flash和RAM容量分为不同型号:

    • 低/中容量(Low/Medium-density):如STM32F103C8T6(64KB Flash),仅支持SPI1SPI2

    • 高容量(High-density):如STM32F103ZE(512KB Flash),额外支持SPI3

  • SPI3是高容量型号的专属外设,STM32F103C8T6作为中容量型号,硬件上未集成SPI3模块。

 SPI寄存器(控制、状态、数据)

 SPI控制寄存器 1(SPI_CR1)(I2 S模式下不使用)

SPI控制寄存器 2(SPI_CR2) 

 SPI 状态寄存器(SPI_SR)

  SPI 数据寄存器(SPI_DR)

SPI的库函数(常用的有三个) 

初始化SPI的函数: 

数据发送的函数 :

数据接收的函数: 

 常用的是:既发送又接受的函数

 模块:W25Q128存储器介绍

电脑保存数据的有RAM、ROM、FLASH (类似于硬盘,断电保存的数据不丢失)

W25Q128是华邦公司推出的一款容量为 128M-bit(相当于 16M-byte)的 SPI 接口的 NOR Flash 芯片。

  • NOR Flash:一种非易失性存储器,它可以在断电或掉电后仍然保持存储的数据,因此被广泛应用于长期数据存储。它具有容量大,可重复擦写、按“扇区/块”擦除的特性。Flash 是有一个物理特性只能写 0 ,不能写 1 ,写 1 靠擦除。(读、改、写)

它还有很多不同容量的好兄弟: 

型号容量
W25Q256256M bits = 32M bytes
W25Q128128M bits = 16M bytes
W25Q6464M bits = 8M bytes
W25Q3232M bits = 4M bytes
W25Q1616M bits = 2M bytes
W25Q808M bits = 1M bytes

 W25Q128模块参数及引脚介绍

 参数:

  • 产品容量:128M-bit(16M-byte)
  • 时钟频率:<=104MHz
  • 工作电压:2.7V ~ 3.6V
  • 工作温度:-40℃ ~ +85℃
  • 支持 SPI 接口

参考接线: 

W25Q128STM32备注
VCC3.3(5.5v会烧掉)电源正极
CSA4 / B12片选信号
DOA6 / B14输出
GNDG电源负极
CLKA5 / B13时钟信号
DIA7 / B15输入

W25Q128存储架构

W25Q128 将 16M 的容量分为 256 个块(block),每块 64K 字节;每块分为 16 个扇区(sector),一扇区 4K 字节;每扇区分为 16 个页(page),一页 256 字节。

 这里不好理解的话,和书进行类比:

W25Q128 256 个(block):每块 64K 字节每块16 个扇区:4K 字节每扇区分为 16 个页(page)一页 256 字节
256 : 每章64K字每章 16小节:4K 字每一小节有16 页一页 256 字

地址范围:

 128M-bit = 16M-byte = 16 x 2^10K-byte = 16 x 2^10 x 2^10 byte = 2^24 byte = 16,777,216 个字节。

        上述中,一个字节代表一个地址,总共是24位的地址,将2^24的字节数量转化成16进制就是0xFFF FFF。所以,地址范围(0x0~0xFFF FFF)。

        W25Q128 的最小擦除单位一个扇区,也就是每次必须擦除 4K 个字节。这样我们需要给 W25Q128 开辟一个至少 4K 的缓存区。 

 W25Q128常用指令

指令(HEX)名称作用
0x06写使能写入数据/擦除之前,必须先发送该指令
0x05读 SR1判定 FLASH 是否处于空闲状态,擦除用
0x03读数据读取数据
0x02页写写入数据,最多写256字节
0x20扇区擦除扇区擦除指令,最小擦除单位

 具体工作时序如下:

  • 写使能 (06H)

执行页写,扇区擦除,块擦除,片擦除,写状态寄存器等指令前,需要写使能。

拉低 CS 片选 → 发送 06H → 拉高 CS 片选

  • 读SR1(05H)

拉低 CS 片选 → 发送 05H → 返回SR1的值 → 拉高 CS 片选

  • 读数据(03H)

拉低 CS 片选 → 发送 03H → 发送24位地址 (封装函数)→ 读取数据(1~n)→ 拉高 CS 片选

  • 页写 (02H)

页写命令最多可以向FLASH传输256个字节的数据。

写使能->拉低 CS 片选 → 发送 02H → 发送24位地址 → 发送数据(1~n)→ 拉高 CS 片选

  • 扇区擦除(20H)

写入数据前,检查内存空间是否全部都是 0xFF ,不满足需擦除

写使能 - 等待空闲-拉低 CS 片选 → 发送擦除 20H→ 发送24位地址 → 拉高 CS 片选-等待空闲

W25Q128状态寄存器​

W25Q128 一共有 3 个状态寄存器,它们的作用是跟踪芯片的状态

这里我们只介绍常用的状态寄存器 1

我们需要记住的是在状态寄存器 1 中:

BUSY:指示当前的状态,0 表示空闲1 表示忙碌

WEL:写使能锁定为 1 时,可以操作页/扇区/块;为 0 时,写禁止。

小实验1:W25Q128的实验(封装SPI接口)

实验目的:

读写W25Q128

硬件清单:

W25Q128、开发板、ST-Link、USB转TTL

硬件接线:

STM32
W25Q128
PA4
CS
PA5
CLK
PA6
DO
PA7
DI
3V3
VCC
GND
GND

w25q128.c文件代码:

流程:

  1. 初始化SPI的函数;
  2. 初始化SPI各个引脚的函数;注意各引脚要求的输入输出模式。
  3. 封装一个发送和接受一个字节的函数:利用HAL_SPI_TransmitRecive()函数实现。
#include "w25q128.h"SPI_HandleTypeDef spi_handle = {0};
void w25q128_spi_init(void){spi_handle.Instance = SPI1;spi_handle.Init.Mode = SPI_MODE_MASTER;    //配置成主模式还是从模式spi_handle.Init.Direction = SPI_DIRECTION_2LINES;                 //配置全双工还是半双工spi_handle.Init.DataSize = SPI_DATASIZE_8BIT;      //数据的长度:8bitspi_handle.Init.CLKPolarity = SPI_POLARITY_LOW;      //CPOL = 0spi_handle.Init.CLKPhase = SPI_PHASE_1EDGE;          //CPHA = 奇数边沿检测spi_handle.Init.NSS = SPI_NSS_SOFT;               //软件控制SS引脚配置spi_handle.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_256;    //波特率分频 :256spi_handle.Init.FirstBit = SPI_FIRSTBIT_MSB;             //高位先行还是低位先行:高位先行//下面这三个先不需要考虑spi_handle.Init.TIMode = SPI_TIMODE_DISABLE;spi_handle.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;spi_handle.Init.CRCPolynomial = 7;HAL_SPI_Init(&spi_handle);
}void HAL_SPI_MspInit(SPI_HandleTypeDef *hspi){if(hspi->Instance == SPI1){__HAL_RCC_GPIOA_CLK_ENABLE();__HAL_RCC_SPI1_CLK_ENABLE();GPIO_InitTypeDef gpio_initstruct;//NSS引脚gpio_initstruct.Pin = GPIO_PIN_4;gpio_initstruct.Mode = GPIO_MODE_OUTPUT_PP;gpio_initstruct.Pull = GPIO_PULLUP;gpio_initstruct.Speed  = GPIO_SPEED_FREQ_HIGH;HAL_GPIO_Init(GPIOA,&gpio_initstruct);//SCL引脚和输出引脚gpio_initstruct.Pin = GPIO_PIN_5 |GPIO_PIN_7;gpio_initstruct.Mode = GPIO_MODE_AF_PP;HAL_GPIO_Init(GPIOA,&gpio_initstruct);//输入引脚gpio_initstruct.Pin = GPIO_PIN_6;gpio_initstruct.Mode = GPIO_MODE_INPUT;HAL_GPIO_Init(GPIOA,&gpio_initstruct);} 
}uint8_t w25q128_spi_swap_byte(uint8_t data){uint8_t recv_data = 0;HAL_SPI_TransmitReceive(&spi_handle,&data, &recv_data,1,1000);  ///size:尺寸代表是多少个字节return recv_data;
}

 小实验2:封装读取芯片ID接口

文件代码:(在实验1的基础上添加)

  • 发送的指令:

  • 返回的数据 

  • 上述中,发送的字节:FLASH_ManufactureDivceID是 0x90。 
  • 上述,由于返回的是两个八位的数,将其保存在一个16位的变量中,因此使用位运算符。

小实验3:读写W25Q128(封装命令接口) 

文件代码:

  • w25q128.c文件代码:

代码配置流程:

  1. 初始化spi函数;
  2. 初始化GPIO的函数MspInit();
  3. 封装数据交换的函数;HAL_SPI_TransmitRecive ( )。
  4. 接下来封装指令的函数:使能、读状态寄存器1(判断busy位,封装等待空闲的函数while())、读数据、页写、擦除等指令。
  5. 在主函数中:擦除、写数据、读数据。
#include "w25q128.h"SPI_HandleTypeDef spi_handle = {0};
void w25q128_spi_init(void){spi_handle.Instance = SPI1;spi_handle.Init.Mode = SPI_MODE_MASTER;    //配置成主模式还是从模式spi_handle.Init.Direction = SPI_DIRECTION_2LINES;                 //配置全双工还是半双工spi_handle.Init.DataSize = SPI_DATASIZE_8BIT;      //数据的长度:8bitspi_handle.Init.CLKPolarity = SPI_POLARITY_LOW;      //CPOL = 0spi_handle.Init.CLKPhase = SPI_PHASE_1EDGE;          //CPHA = 奇数边沿检测spi_handle.Init.NSS = SPI_NSS_SOFT;               //软件控制SS引脚配置spi_handle.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_256;    //波特率分频 :256spi_handle.Init.FirstBit = SPI_FIRSTBIT_MSB;             //高位先行还是低位先行:高位先行//下面这三个先不需要考虑spi_handle.Init.TIMode = SPI_TIMODE_DISABLE;spi_handle.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;spi_handle.Init.CRCPolynomial = 7;HAL_SPI_Init(&spi_handle);
}void HAL_SPI_MspInit(SPI_HandleTypeDef *hspi){if(hspi->Instance == SPI1){__HAL_RCC_GPIOA_CLK_ENABLE();__HAL_RCC_SPI1_CLK_ENABLE();GPIO_InitTypeDef gpio_initstruct;//NSS引脚gpio_initstruct.Pin = GPIO_PIN_4;gpio_initstruct.Mode = GPIO_MODE_OUTPUT_PP;gpio_initstruct.Pull = GPIO_PULLUP;gpio_initstruct.Speed  = GPIO_SPEED_FREQ_HIGH;HAL_GPIO_Init(GPIOA,&gpio_initstruct);//SCL引脚和输出引脚gpio_initstruct.Pin = GPIO_PIN_5 |GPIO_PIN_7;gpio_initstruct.Mode = GPIO_MODE_AF_PP;HAL_GPIO_Init(GPIOA,&gpio_initstruct);//输入引脚gpio_initstruct.Pin = GPIO_PIN_6;gpio_initstruct.Mode = GPIO_MODE_INPUT;HAL_GPIO_Init(GPIOA,&gpio_initstruct);} 
}uint8_t w25q128_spi_swap_byte(uint8_t data){uint8_t recv_data = 0;HAL_SPI_TransmitReceive(&spi_handle,&data, &recv_data,1,1000);  ///size:尺寸代表是多少个字节return recv_data;
}//初始化w25q128模块
void w25q128_init(void){w25q128_spi_init();
}//测试:读ID
uint16_t w25q128_read_id(void){uint16_t device_id = 0;W25Q128_CS(0);w25q128_spi_swap_byte(FLASH_ManufactDeviceID);w25q128_spi_swap_byte(0x00);w25q128_spi_swap_byte(0x00);w25q128_spi_swap_byte(0x00);device_id = w25q128_spi_swap_byte(FLASH_DummyBtye) << 8;  /* 将数据放在高8位 */device_id |= w25q128_spi_swap_byte(FLASH_DummyBtye);      /* 利用 |= 将数据放在低8位,并保留高8位的数据 */W25Q128_CS(1);return device_id; 
}//写使能void w25q128_write_enable(void)
{W25Q128_CS(0);w25q128_spi_swap_byte(FLASH_WriteEable);W25Q128_CS(1);
}//读SR1寄存器
uint8_t w25q128_read_sr1(void){uint8_t recv_data = 0;W25Q128_CS(0);w25q128_spi_swap_byte(FLASH_ReadStatusReg1);recv_data = w25q128_spi_swap_byte(FLASH_DummyBtye);W25Q128_CS(1);return recv_data;
}//发送地址的函数
void w25q128_send_address(uint32_t address){   //地址是3个字节,先发送高位,在发送中位,最后发送低位w25q128_spi_swap_byte(address >> 16);//高位w25q128_spi_swap_byte(address >> 8);  //中位:由于函数是一个8位的,因此,移动后数据后,高位自动去掉。w25q128_spi_swap_byte(address);
}//忙等待的函数
void w25q128_wait_busy(void){while ((w25q128_read_sr1() & 0x01) == 0x01);  //判断最后一位是不是1
}//读数据
void w25q128_read_data(uint32_t address,uint8_t *data,uint32_t size){uint32_t i = 0;W25Q128_CS(0);w25q128_spi_swap_byte(FLASH_ReadDate);w25q128_send_address(address);for(i = 0;i< size; i++)data[i] =  w25q128_spi_swap_byte(FLASH_DummyBtye);W25Q128_CS(1);}
//页写:写的是256个字节,
void w25q128_write_page(uint32_t address,uint8_t *data,uint16_t size){   //代表的是字节数量uint16_t i = 0;w25q128_write_enable();W25Q128_CS(0);w25q128_spi_swap_byte(FLASH_PageProgram);w25q128_send_address(address);for(i = 0;i < size; i++)w25q128_spi_swap_byte(data[i]);W25Q128_CS(1);//忙等待,写入数据是需要花费时间的;看状态寄存器的最后一位是0还是1w25q128_wait_busy();
}
//扇区擦除
void w25q128_erase_sector(uint32_t address){w25q128_write_enable();w25q128_wait_busy();W25Q128_CS(0);w25q128_spi_swap_byte(FLASH_SectorErase);w25q128_send_address(address);W25Q128_CS(1);w25q128_wait_busy();
}
//如何指定变量的数据类型是多少
  • w25128.h文件代码 
  • 定义一个宏函数:读取CS引脚的电平,是高电平还是低电平;
  • 将指令表进行宏定义,便于读写方便。
#ifndef __W25Q128_H__
#define __W25Q128_H__
#include "stm32f1xx.h"#define W25Q128_CS(x)  do{x ? \HAL_GPIO_WritePin(GPIOA,GPIO_PIN_4,GPIO_PIN_SET): \HAL_GPIO_WritePin(GPIOA,GPIO_PIN_4,GPIO_PIN_RESET);\}while(0)/* 指令表 */
#define FLASH_ManufactDeviceID          0x90
#define FLASH_WriteEable                0x06
#define FLASH_ReadStatusReg1            0x05
#define FLASH_ReadDate                  0x03
#define FLASH_PageProgram               0x02
#define FLASH_SectorErase               0x20
#define FLASH_DummyBtye                 0xFF                       void w25q128_init(void);
uint16_t w25q128_read_id(void);
void w25q128_read_data(uint32_t address,uint8_t *data,uint32_t size);
void w25q128_write_page(uint32_t address,uint8_t *data,uint16_t size);
void w25q128_erase_sector(uint32_t address);                       
#endif
  • main.c文件代码 
#include "sys.h"
#include "led.h"
#include "delay.h"
#include "uart1.h"
#include "w25q128.h"
uint8_t data_write[4] = {0xAA,0xBB,0xEE,0xDD};
uint8_t data_read[4] = {0};int main(void)
{HAL_Init();                         /* 初始化HAL库 */stm32_clock_init(RCC_PLL_MUL9);     /* 设置时钟, 72Mhz */led_init();                         /* LED初始化 */uart1_init(115200);printf("hello,world\r\n");w25q128_init();uint16_t device_id = w25q128_read_id();printf("device_id = %X \r\n",device_id);  /* %X是16进制,返回EF17 */w25q128_erase_sector(0x000000);w25q128_write_page(0x000000,data_write,4);w25q128_read_data(0x000000,data_read,4);printf("data_read: %X,%X,%X,%X\r\n",data_read[0],data_read[1],data_read[2],data_read[3]);while(1){}
}

总结: 

  • 相关的位运算符的使用:判断第一位是0 还是 1: & =  0x01<<、将数据放在高8位 << 8、将数据放在低八位利用  “|  =”  ;
  • 熟悉模块的相关指令;读、写、擦除、读寄存器状态、使能。

相关文章:

STM32中的SPI通信协议

IIC和SPI的对比 IIC是半双工的通信&#xff0c;无法同时收发信息&#xff1b;SPI是全双工通讯&#xff0c;可以同时收发信息&#xff1b;IIC的通讯协议较复杂&#xff0c;而SPI通讯协议较简单&#xff1b;IIC需要通过地址选择从机&#xff0c;而SPI只主要一个引脚即可选中从机…...

从版本控制到协同开发:深度解析 Git、SVN 及现代工具链

前言&#xff1a;在当今软件开发的浪潮中&#xff0c;版本控制与协同开发无疑扮演着举足轻重的角色。从最初的单兵作战到如今大规模团队的高效协作&#xff0c;一套成熟且得力的版本控制系统以及围绕其构建的现代工具链&#xff0c;已然成为推动软件项目稳步前行的关键引擎。今…...

redis Pub/Sub 简介 -16 (PUBLISH、SUBSCRIBE、PSUBSCRIBE)

Redis Pub/Sub 简介&#xff1a;PUBLISH、SUBSCRIBE、PSUBSCRIBE Redis Pub/Sub 是一种强大的消息传递范例&#xff0c;可在应用程序的不同部分之间实现实时通信。它是构建可扩展和响应式系统的基石&#xff0c;允许组件在没有直接依赖的情况下进行交互。本章将全面介绍 Redis…...

《黄帝内经》数学建模与形式化表征方式的重构

黄帝内经的数学概括&#xff1a;《黄帝内经》数学建模与形式化表征方式的重构 摘要&#xff1a;《黄帝内经》通过现代数学理论如动力系统、代数拓扑和随机过程&#xff0c;被重构为一个形式化的人体健康模型。该模型包括阴阳动力学的微分几何、五行代数的李群结构、经络拓扑与同…...

PyTorch Image Models (timm) 技术指南

timm PyTorch Image Models (timm) 技术指南功能概述 一、引言二、timm 库概述三、安装 timm 库四、模型加载与推理示例4.1 通用推理流程4.2 具体模型示例4.2.1 ResNeXt50-32x4d4.2.2 EfficientNet-V2 Small 模型4.2.3 DeiT-3 large 模型4.2.4 RepViT-M2 模型4.2.5 ResNet-RS-1…...

基于Scikit-learn与Flask的医疗AI糖尿病预测系统开发实战

引言 在精准医疗时代&#xff0c;人工智能技术正在重塑临床决策流程。本文将深入解析如何基于MIMIC-III医疗大数据集&#xff0c;使用Python生态构建符合医疗AI开发规范的糖尿病预测系统。项目涵盖从数据治理到模型部署的全流程&#xff0c;最终交付符合DICOM标准的临床决策支…...

掌握聚合函数:COUNT,MAX,MIN,SUM,AVG,GROUP BY和HAVING子句的用法,Where和HAVING的区别

对于Java后端开发来说&#xff0c;必须要掌握常用的聚合函数&#xff1a;COUNT&#xff0c;MAX&#xff0c;MIN&#xff0c;SUM&#xff0c;AVG&#xff0c;掌握GROUP BY和HAVING子句的用法&#xff0c;掌握Where和HAVING的区别&#xff1a; ✅ 一、常用聚合函数&#xff08;聚…...

【Node.js】高级主题

个人主页&#xff1a;Guiat 归属专栏&#xff1a;node.js 文章目录 1. Node.js 高级主题概览1.1 高级主题架构图 2. 事件循环与异步编程深度解析2.1 事件循环机制详解事件循环阶段详解 2.2 异步编程模式演进高级异步模式实现 3. 内存管理与性能优化3.1 V8 内存管理机制内存监控…...

【Linux】定时任务 Crontab 与时间同步服务器

目录 一、用户定时任务的创建与使用 1.1 用户定时任务的使用技巧 1.2 管理员对用户定时任务的管理 1.3 用户黑白名单的管理 一、用户定时任务的创建与使用 1.1 用户定时任务的使用技巧 第一步&#xff1a;查看服务基本信息 systemctl status crond.service //查看周期性…...

【TCP/IP协议族详解】

目录 第1层 链路/网络接口层—帧&#xff08;Frame&#xff09; 1. 链路层功能 2. 常见协议 2.1. ARP&#xff08;地址解析协议&#xff09; 3. 常见设备 第2层 网络层—数据包&#xff08;Packet&#xff09; 1. 网络层功能 2. 常见协议 2.1. ICMP&#xff08;互联网…...

蓝桥杯电子赛_零基础利用按键实现不同数字的显现

目录 一、前提 二、代码配置 bsp_key.c文件 main.c文件 main.c文件的详细讲解 功能实现 注意事项 一、前提 按键这一板块主要是以记忆为主&#xff0c;我直接给大家讲解代码去实现我要配置的功能。本次我要做的项目是板子上的按键有S4~S19&#xff0c;我希望任意一个按键…...

Docker架构详解

一,Docker的四大要素&#xff1a;Dockerfile、镜像(image)、容器(container)、仓库(repository) 1.dockerfile&#xff1a;在dockerfile文件中写构建docker的命令,通过dockerbuild构建image 2.镜像&#xff1a;就是一个只读的模板&#xff0c;镜像可以用来创建docker容器&…...

Rust 学习笔记:关于生命周期的练习题

Rust 学习笔记&#xff1a;关于生命周期的练习题 Rust 学习笔记&#xff1a;关于生命周期的练习题生命周期旨在防止哪种编程错误&#xff1f;以下代码能否通过编译&#xff1f;若能&#xff0c;输出是&#xff1f;如果一个引用的生命周期是 static&#xff0c;这意味着什么&…...

Spring AI 模块架构与功能解析

Spring AI 是 Spring 生态系统中的一个新兴模块&#xff0c;专注于简化人工智能和机器学习技术在 Spring 应用程序中的集成。本文将详细介绍 Spring AI 的核心组件、功能模块及其之间的关系&#xff0c;帮助具有技术基础的读者快速了解和应用 Spring AI。 Spring AI 的核心概念…...

单元测试学习笔记

单元测试是软件测试的基础层级&#xff0c;主要针对代码的最小可测试单元进行验证。单元测试可以帮助快速定位问题边界&#xff0c;提升代码可维护性&#xff0c;支持安全的重构操作。 测试对象&#xff1a; 独立函数/方法纯工具类&#xff08;如数据处理函数&#xff09;UI组…...

多模态大语言模型arxiv论文略读(九十)

Hybrid RAG-empowered Multi-modal LLM for Secure Data Management in Internet of Medical Things: A Diffusion-based Contract Approach ➡️ 论文标题&#xff1a;Hybrid RAG-empowered Multi-modal LLM for Secure Data Management in Internet of Medical Things: A Di…...

(1-6-1)Java 集合

目录 0.知识概述&#xff1a; 1.集合 1.1 集合继承关系类图 1.2 集合遍历的三种方式 1.3 集合排序 1.3.1 Collections实现 1.3.2 自定义排序类 2 List 集合概述 2.1 ArrayList &#xff08;1&#xff09;特点 &#xff08;2&#xff09;常用方法 2.2 LinkedList 3…...

spring5-配外部文件-spEL-工厂bean-FactoryBean-注解配bean

spring配外部文件 我们先在Spring里配置一个数据源 1.导c3p0包,这里我们先学一下hibernate持久化框架&#xff0c;以后用mybites. <dependency><groupId>org.hibernate</groupId><artifactId>hibernate-core</artifactId><version>5.2.…...

[安全清单] Linux 服务器安全基线:一份可以照着做的加固 Checklist

更多服务器知识&#xff0c;尽在hostol.com 嘿&#xff0c;各位服务器的“守护者”们&#xff01;当你拿到一台崭新的Linux服务器&#xff0c;或者接手一台正在运行的服务器时&#xff0c;脑子里是不是会闪过一丝丝关于安全的担忧&#xff1f;“我的服务器安全吗&#xff1f;”…...

企业级单元测试流程

企业级的单元测试流程不仅是简单编写测试用例&#xff0c;而是一整套系统化、自动化、可维护、可度量的工程实践&#xff0c;贯穿从代码编写到上线部署的全生命周期。下面是一个尽可能完善的 企业级单元测试流程设计方案&#xff0c;适用于 Java 生态&#xff08;JUnit Mockit…...

安卓开发用到的设计模式(2)结构型模式

安卓开发用到的设计模式&#xff08;2&#xff09;结构型模式 文章目录 安卓开发用到的设计模式&#xff08;2&#xff09;结构型模式1. 适配器模式&#xff08;Adapter Pattern&#xff09;2. 装饰器模式&#xff08;Decorator Pattern&#xff09;3. 代理模式&#xff08;Pro…...

JavaWeb:SpringBoot配置优先级详解

3种配置 打包插件 命令行 优先级 SpringBoot的配置优先级决定了不同配置源之间的覆盖关系&#xff0c;遵循高优先级配置覆盖低优先级的原则。以下是详细的优先级排序及配置方法说明&#xff1a; 一、配置优先级从高到低排序 1.命令行参数 优先级最高&#xff0c;通过keyvalu…...

故障率预测:基于LSTM的GPU集群硬件健康监测系统(附Prometheus监控模板)

一、GPU集群健康监测的挑战与价值 在大规模深度学习训练场景下&#xff0c;GPU集群的硬件故障率显著高于传统计算设备。根据2023年MLCommons统计&#xff0c;配备8卡A100的服务器平均故障间隔时间&#xff08;MTBF&#xff09;仅为1426小时&#xff0c;其中显存故障占比达38%&…...

【b站计算机拓荒者】【2025】微信小程序开发教程 - chapter3 项目实践 -1 项目功能描述

1 项目功能描述 # 智慧社区-小程序-1 欢迎页-加载后端&#xff1a;动态变化-2 首页-轮播图&#xff1a;动态-公共栏&#xff1a;动态-信息采集&#xff0c;社区活动&#xff0c;人脸检测&#xff0c;语音识别&#xff0c;心率检测&#xff0c;积分商城-3 信息采集页面-采集人数…...

FFmpeg 安装包全攻略:gpl、lgpl、shared、master 区别详解

这些 FFmpeg 安装包有很多版本和变种&#xff0c;主要区别在于以下几个方面&#xff1a; ✅ 一、从名称中看出的关键参数&#xff1a; 1. 版本号 master&#xff1a;开发版&#xff0c;最新功能&#xff0c;但可能不稳定。n6.1 / n7.1&#xff1a;正式版本&#xff0c;更稳定…...

AI浪潮下,媒体内容运营的五重变奏

算法驱动的个性化推荐 在信息爆炸的时代,用户面临着海量的内容选择,如何让用户快速找到感兴趣的人工智能内容,成为媒体运营的关键。算法驱动的个性化推荐模式应运而生,它通过分析用户的行为数据,如浏览历史、点赞、评论、搜索关键词等,构建用户兴趣画像 ,再依据画像为用…...

WindTerm 以 SSH 协议的方式通过安恒明御堡垒机间接访问服务器

1. 配置堡垒机秘钥 创建公私钥ssh-keygen -t rsa -b 4096执行完该命令后按照提示一路回车就能够创建出公私钥注意&#xff1a;在创建过程中会让你指定秘钥的存储位置以及对应的密码&#xff0c;最好自行指定一下 id_rsa 是私钥id_rsa.pub 是公钥 在堡垒机中指定创建好的私钥 …...

通过现代数学语言重构《道德经》核心概念体系,形成一个兼具形式化与启发性的理论框架

以下是对《道德经》的数学转述尝试&#xff0c;通过现代数学语言重构其核心概念&#xff0c;形成一个兼具形式化与启发性的理论框架&#xff1a; 0. 基础公理体系 定义&#xff1a; 《道德经》是一个动态宇宙模型 U(D,V,Φ)&#xff0c;其中&#xff1a; D 为“道”的无限维…...

邂逅Node.js

首先先要来学习一下nodejs的基础&#xff08;和后端开发有联系的&#xff09; 再然后的学习路线是学习npm&#xff0c;yarn&#xff0c;cnpm&#xff0c;npx&#xff0c;pnpm等包管理工具 然后进行模块化的使用&#xff0c;再去学习webpack和git&#xff08;版本控制工具&…...

计算机视觉(图像算法工程师)学习路线

计算机视觉学习路线 Python基础 常量与变量 列表、元组、字典、集合 运算符 循环 条件控制语句 函数 面向对象与类 包与模块Numpy Pandas Matplotlib numpy机器学习 回归问题 线性回归 Lasso回归 Ridge回归 多项式回归 决策树回归 AdaBoost GBDT 随机森林回归 分类问题 逻辑…...