STM32IIC与SPI详解
单片机里的通信协议其实蛮多的,IIC;SPI;MQTT;CAN;包括串口也是一种通信协议。而串口通信虽然实现了全双工,但需要至少三根线,为了节省这一根线的成本,于是IIC诞生了。
目录
一.IIC协议
1.IIC的结构
2.IIC的特点
3.IIC的通信时序
4.具体配置(32HAL库版)
二.SPI协议
1.SPI的结构
2.SPI的特点
3.具体配置
三:联合配置
1.OLED屏幕的配置
OLED的数据格式
OLED的写入模式
Oled的命令
代码:
W25Q128配置
W25Q128的命令
W25Q128的寄存器
代码:
最终配置:
祝你看完就会
一.IIC协议
IIC协议其实就是一种标准外设协议,其实所谓协议,本质上就是各种时序电路的组合。IIC也不例外,它的最大特点就是特别的轻量级。
1.IIC的结构
IIC的简便和轻量级就在于它只有两条线,一条是时钟线SCL,一条是数据线SDA。说白了,就是在SCL的控制下在SDA上传输命令/数据。
在这张图上可以看到同时有很多设备连接在这两条线上,它们之间的关系一般是一主多从。
2.IIC的特点
1.IIC是半双工通信,因为SCL不负责传输数据,只有一条SDA数据线,无法发的同时接收信息。
2.如果你使用的是STM32芯片,那么如果没有配置上拉电阻的话GPIO口必须配置为开漏输出。因为假设时序出现错误两个设备一个讲SDA拉低一个将SDA拉高,那么将导致短路。
3.同步传输:数据的传输是严格按照时钟线来进行的。
3.IIC的通信时序
IIC的通信主要依靠四个信号:起始信号,应答信号,停止信号;读写信号;
START:
STOP:
ASK:
读写:
其中ASK就是在在吧SCL拉高的期间读取SDA的的电平。读取完成后立即拉低。读取到的SDA假设为1则表示有应答,若为0则表示没有应答。
读写信号则是在起始信号后将SCL拉低的期间向SDA上放或者读取数据。
START和STOP比较简单不多赘述。
4.具体配置(32HAL库版)
头文件:
#ifndef __IIC_H__
#define __IIC_H__#include "sys.h"#define SDA_PULL_UP() HAL_GPIO_WritePin(GPIO_POTT, SDA, GPIO_PIN_SET)
#define SDA_PULL_DOWN() HAL_GPIO_WritePin(GPIO_POTT, SDA, GPIO_PIN_RESET)
#define SCL_PULL_UP() HAL_GPIO_WritePin(GPIO_POTT, SCL, GPIO_PIN_SET)
#define SCL_PULL_DOWN() HAL_GPIO_WritePin(GPIO_POTT, SCL, GPIO_PIN_RESET)void iic_Stop(GPIO_TypeDef *GPIO_POTT,uint16_t SDA,uint16_t SCL);
void iic_Start(GPIO_TypeDef *GPIO_POTT,uint16_t SDA,uint16_t SCL);
void iic_Ask(GPIO_TypeDef *GPIO_POTT,uint16_t SDA,uint16_t SCL);
void iic_Sendbyte(GPIO_TypeDef *GPIO_POTT,uint16_t SDA,uint16_t SCL,uint8_t DATA);#endif
首先我这里用了大量的宏定义了很多宏函数来方便控制对应线的电平高低。另外如果你看的细致的话会发现我这里宏函数中的参数也是宏,然而你却在这儿找不到这个宏。其实它并不是宏,我用了一种稍微怪的方式来简便我在IIC定义里的工作量,但加大了一点调用的工作量。
源文件:
#include "IIC.h"void iic_Stop(GPIO_TypeDef *GPIO_POTT,uint16_t SDA,uint16_t SCL){SCL_PULL_UP();SDA_PULL_DOWN(); //先拉高SCL再拉高SDASDA_PULL_UP();}void iic_Start(GPIO_TypeDef *GPIO_POTT,uint16_t SDA,uint16_t SCL){SCL_PULL_UP(); //SCL高电平期间SDA下降沿SDA_PULL_UP();SDA_PULL_DOWN();SCL_PULL_DOWN();
}void iic_Ask(GPIO_TypeDef *GPIO_POTT,uint16_t SDA,uint16_t SCL){SCL_PULL_UP(); SCL_PULL_DOWN();
}void iic_Sendbyte(GPIO_TypeDef *GPIO_POTT,uint16_t SDA,uint16_t SCL,uint8_t DATA){uint8_t i,tmp = DATA;//主机在SCL低电平期间在SDA上放数据for(i = 0; i<8; i++){if((tmp & 0x80) == 0x80) //DATA &= 1000 0000 SDA_PULL_UP();elseSDA_PULL_DOWN();SCL_PULL_UP(); //从机在SCL高电平期间读取这一位 SCL_PULL_DOWN();tmp = tmp << 1;}
}
想必如果你由上面的疑问看了源文件也就理解 ,我定义的宏函数里的参数并不是宏而是调用它的函数里的参数。所以我增加的调用方的工作量就是传参很多,IIC这里就非常简介易懂了。
另外一个点就是数据传输函数中是一位一位放的,不断地放不断地左移知道8次轮回完成一字节的传输则结束。
二.SPI协议
SPI的全称是Serial Peripheral Interface。有基础的人估计一眼就看见Serial(串口)了。没错,其实这东西它使用非常像串口。不仅如此,配置起来也非常像串口。
1.SPI的结构
SPI一共由四条线组成:SCK;MISO;MOSI;NSS(CS)
其中,MISO和MOSI分别是:Master Input Slave Output
这里某些同学不要想歪了哈,Master这里就是主机,Slave就是从机,所以这两根线就很好理解了,简单说就是RX和TX一样。
然而:虽然这里也是两条线也是全双工,但是SPI的传输数据方式却是非常的特殊的。
图中画圈的地方都是SPI的重点,其中特殊就在于它的位移寄存器 。
2.SPI的特点
1.SPI最大的特点就来自于它的位移寄存器,它每发送一字节的数据就必须收一字节的数据,同样的,它要收一字节的数据就必须发出一字节数据。
2.看了特点一你会认为SPI是强制全双工的,但其实不是,它完全可以配置为半双工或者只有一条数据线。
3.SPI的工作模式比较特别,它的工作模式取决于时钟极性和时钟相位
第三点展开来说:
时钟极性控制SPI数据线上没有数据时SCL的电平状态:
CPOL为0则空闲时为低电平,反之则为高电平。
CPHA为0则每一个奇数边缘采样数据(第一个),反之则为偶数边缘采样。
你要是看不懂也没关系,总之就是CPOL和CPHA排列组合一共有四种工作模式。
3.具体配置
SPI的配置一般是依靠板子上确定的外设的,因为它不像IIC那样的轻量级。它有很多的东西需要配,比如:分频数;时钟启动;工作模式;是否半双工等等。
#include "SPI.h"
SPI_HandleTypeDef spi_handle = {0};void 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;spi_handle.Init.CLKPolarity = SPI_POLARITY_LOW; //低电平有效spi_handle.Init.CLKPhase = SPI_PHASE_1EDGE; //奇数取值spi_handle.Init.NSS = SPI_NSS_SOFT; //软件调控NSSspi_handle.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_256; //分频数选择spi_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 *spi_handle){if(spi_handle->Instance == SPI1){GPIO_InitTypeDef gpio_initstruct;__HAL_RCC_GPIOA_CLK_ENABLE(); __HAL_RCC_SPI1_CLK_ENABLE();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);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 SPI_swap_byte(uint8_t data){uint8_t rev_data = 0;HAL_SPI_TransmitReceive(&spi_handle,&data,&rev_data,1,1000);return rev_data;
}
这里对于Instance的配置还是挺繁琐的,但是也只是繁琐并没有难度。剩下的就是时钟;GPIO配置;然后可以看到在发送的部分它的函数叫HAL_SPI_TransmitReceive,对应上了我们说的接收的同时必须发送。
三:联合配置
两个协议都学会了,如何应用呢?IIC的入门外设还是非常经典的--OLED屏幕。那SPI呢?可以采用一块存储块进行配合读写。我这里就用W25Q128的存储模块。
1.OLED屏幕的配置
Oled的配置核心其实就是显示东西嘛,所以其实总结成一句话就是:
告诉屏幕在哪里显示什么东西。
OLED是只可以选择亮或者灭的,所以显示什么东西其实最终说白了是各种点阵,这个东西其实没啥技术含量。另外OLED模块的初始化也是不需要学的,你就照着把一堆命令直接复制过来用就行了。所以主要的配置重点在于:如何告诉他。
那么这个时候我们就需要读一下OLED的手册了。
OLED的数据格式
以上为手册的原图,可以看到它的发送全部都是 Control byte + Data byte。那么这是什么意思呢?
说白了,就是Control byte 用来让Oled判断接下来所接收的数据到底是命令还是显示数据。
可以看到,整个数据帧的格式就是:
Start信号--写入数据模式--ASK--Control byte--Data byte--ASK--Stop信号
那么此时的问题就变成了写入模式是什么?以及Control byte是什么?
Control byte看右下角,D/C的后一位写0则表示接下来的数据是Command,写1则表示接下来的数据是Data。
其实自己看手册可绝望了,不信? 给:
OLED的写入模式
一共有四种:
总而言之呢,其实你只记得住第一个就行,因为我们用哪一个都ok,第一个在不配置的情况下是被默认选定的。只需要知道它在写完后会自动向右偏移一位。当写道最右边后会返回来这一行的最左边。那么这里就需要稍微知道一下屏幕的大小了:
整个屏幕是128*64的,每一个字节的八位是竖着排列的,逻辑为1的就亮为0就灭。
64 / 8 = 8,这也是为什么是8个page。
Oled的命令
Oled其实内置的是一块芯片,所以它的命令其实还是蛮多的。但是可以看到这里我给你截下来的命令都是关于位置设定和写入模式的设定的。
其实这里主要的就是Page和Column 的设定,Page的很简单B0~B7分别表示Page0到Page7.
主要就是Column需要给两次,因为有 128位嘛,所以需要两个字节。这就在编程的方面稍微有点小麻烦。
代码:
#include "oled.h"
#include "delay.h"
#include "front.h"void OLED_INIT(){GPIO_INIT();delay_ms(100); Oled_Write_Cmd(0xAE); Oled_Write_Cmd(0xD5); Oled_Write_Cmd(0x80); Oled_Write_Cmd(0xA8); Oled_Write_Cmd(0x3F); Oled_Write_Cmd(0xD3); Oled_Write_Cmd(0x00); Oled_Write_Cmd(0x40); Oled_Write_Cmd(0xA1); Oled_Write_Cmd(0xC8); Oled_Write_Cmd(0xDA); Oled_Write_Cmd(0x12);Oled_Write_Cmd(0x81); Oled_Write_Cmd(0xCF); Oled_Write_Cmd(0xD9); Oled_Write_Cmd(0xF1);Oled_Write_Cmd(0xDB); Oled_Write_Cmd(0x30);Oled_Write_Cmd(0xA4); Oled_Write_Cmd(0xA6); Oled_Write_Cmd(0x8D); Oled_Write_Cmd(0x14);Oled_Write_Cmd(0xAF); }void GPIO_INIT(){GPIO_InitTypeDef gpio_init;gpio_init.Mode = GPIO_MODE_OUTPUT_PP;gpio_init.Pin = SDA_PIN|SCL_PIN;gpio_init.Pull = GPIO_PULLUP;gpio_init.Speed = GPIO_SPEED_FREQ_HIGH;__HAL_RCC_GPIOB_CLK_ENABLE();HAL_GPIO_Init(GPIOB, &gpio_init);
}void Oled_Write_Cmd(uint8_t cmd){iic_Start(SDA_PORT,SDA_PIN,SCL_PIN);iic_Sendbyte(SDA_PORT,SDA_PIN,SCL_PIN,0x78);iic_Ask(SDA_PORT,SDA_PIN,SCL_PIN);iic_Sendbyte(SDA_PORT,SDA_PIN,SCL_PIN,0x00);iic_Ask(SDA_PORT,SDA_PIN,SCL_PIN);iic_Sendbyte(SDA_PORT,SDA_PIN,SCL_PIN,cmd);iic_Ask(SDA_PORT,SDA_PIN,SCL_PIN);iic_Stop(SDA_PORT,SDA_PIN,SCL_PIN);
}void Oled_Write_Data(uint8_t data){iic_Start(SDA_PORT,SDA_PIN,SCL_PIN);iic_Sendbyte(SDA_PORT,SDA_PIN,SCL_PIN,0x78);iic_Ask(SDA_PORT,SDA_PIN,SCL_PIN);iic_Sendbyte(SDA_PORT,SDA_PIN,SCL_PIN,0x40);iic_Ask(SDA_PORT,SDA_PIN,SCL_PIN);iic_Sendbyte(SDA_PORT,SDA_PIN,SCL_PIN,data);iic_Ask(SDA_PORT,SDA_PIN,SCL_PIN);iic_Stop(SDA_PORT,SDA_PIN,SCL_PIN);
}void Oled_Set_Position(uint8_t page,uint8_t column){Oled_Write_Cmd(0xB0 + page); //选择页数Oled_Write_Cmd(column & 0x0F); //低四位Oled_Write_Cmd(((column & 0xF0) >> 4) | 0x10); //高四位}void Oled_clear(void){uint8_t i,j; for(i = 0;i < 8;i++){ Oled_Set_Position(i,0);for(j = 0;j<128;j++){Oled_Write_Data(0);} }}void Oled_show_picture(uint8_t hight,uint8_t wide,uint8_t *picture){uint8_t page,i,j;page = hight / 8;for(i = 0; i<page; i++){Oled_Set_Position(i,0);for(j = 0; j<wide; j++){Oled_Write_Data(picture[wide * i + j]);}}}void Oled_show_char(uint8_t Ocolumn, uint8_t Opage, uint8_t num, uint8_t size)
{uint8_t i, j, page;num = num - ' ';page = size / 8;if(size % 8)page++;for(j = 0; j < page; j++){Oled_Set_Position(Opage + j,Ocolumn);for(i = size / 2 * j; i < size /2 * (j + 1); i++){if(size == 12)Oled_Write_Data(ascii_6X12[num][i]);else if(size == 16)Oled_Write_Data(ascii_8X16[num][i]);else if(size == 24)Oled_Write_Data(ascii_12X24[num][i]);}}
}void Oled_show_string(uint8_t column, uint8_t page, char *p, uint8_t size)
{while(*p != '\0'){Oled_show_char(column, page, *p, size);column += size/2;p++;}
}
W25Q128配置
对于它其实主要就是配置 读;写;等待空闲;这就是一个FLASH储存器。
FLASH的唯一特性就是:它只能写0不能写1.
并且该模块不需要初始化,命令同样很多但是我们用的很少。
W25Q128的命令
0x06 | 写使能 | 写入数据/擦除之前,必须先发送该指令 |
0x05 | 读 SR1 | 判定 FLASH 是否处于空闲状态,擦除用 |
0x03 | 读数据 | 读取数据 |
0x02 | 页写 | 写入数据,最多写256字节 |
0x20 | 扇区擦除 | 扇区擦除指令,最小擦除单位 |
这里主要讲一些要点:
1.在做任何通讯的操作之前,必须要拉低CS,也就是拉低片选。这也是它和IIC不同的地方,IIC是进行寻址,而它通过拉低片选。操作完成后必须再拉高。
2.在进行读/写操作是需要发送地址,这个地址是三字节的,而发送时每次发送的是一个字节,所以就需要位操作。
void send_addr(uint32_t address){SPI_swap_byte((uint8_t)address >> 16); //右移的同时进行强转,强转保留低位,所以这里是发送高位SPI_swap_byte((uint8_t)address >> 8); //中间8位SPI_swap_byte((uint8_t)address); //低8位
}
3.任何和写相关的操作比如:马上要写,刚刚写完;必须进行等待空闲。
W25Q128的寄存器
这里就介绍这个一个,就是为了等待空闲操作使用。
在编程过程中,步骤也很简单:
拉低片选----向芯片发送读取Busy的命令----发送的同时接收----while来阻塞知道接收到BusyFlag为0----拉高片选。
void wait_busy(void){W25Q128_CS(0);SPI_swap_byte(FLASH_ReadStatusReg1); //读取状态寄存器指令SPI_swap_byte(FLASH_DummyByte); //接收状态寄存器flag while((SPI_swap_byte(0xFF) & 0x01) == 1); //等待知道寄存器busy位变为0 W25Q128_CS(1);
}
代码:
#include "w25q128.h"uint16_t w25q128_config(void){uint16_t device_id = 0;W25Q128_CS(0);SPI_swap_byte(FLASH_ManufactDeviceID);SPI_swap_byte(0x00);SPI_swap_byte(0x00);SPI_swap_byte(0x00);device_id = SPI_swap_byte(FLASH_DummyByte) << 8;device_id |= SPI_swap_byte(FLASH_DummyByte);W25Q128_CS(1);return device_id;
}void wait_busy(void){W25Q128_CS(0);SPI_swap_byte(FLASH_ReadStatusReg1); //读取状态寄存器指令SPI_swap_byte(FLASH_DummyByte); //接收状态寄存器flag while((SPI_swap_byte(0xFF) & 0x01) == 1); //等待知道寄存器busy位变为0 W25Q128_CS(1);
}void send_addr(uint32_t address){SPI_swap_byte((uint8_t)address >> 16); //右移的同时进行强转,强转保留低位,所以这里是发送高位SPI_swap_byte((uint8_t)address >> 8); //中间8位SPI_swap_byte((uint8_t)address); //低8位
}void w25q128_writ_enable(void)
{W25Q128_CS(0);SPI_swap_byte(FLASH_WriteEnable);W25Q128_CS(1);
}void read_data(uint32_t address, uint8_t *rev_data, uint8_t size){W25Q128_CS(0);SPI_swap_byte(FLASH_ReadData);send_addr(address);uint8_t i = 0; for(i = 0; i < size; i++){rev_data[i] = SPI_swap_byte(0xFF); //直接根据指针写进去了所以不需要返回值}W25Q128_CS(1);
}void write_page_data(uint32_t address, uint8_t *write_data, uint8_t size){ w25q128_writ_enable();wait_busy();W25Q128_CS(0);SPI_swap_byte(FLASH_PageProgram);send_addr(address);uint16_t i = 0;for(i = 0; i< size; i++){SPI_swap_byte(write_data[i]);}W25Q128_CS(1);wait_busy();}void erase_page(uint32_t address){w25q128_writ_enable();wait_busy();W25Q128_CS(0);SPI_swap_byte(FLASH_SectorErase);send_addr(address);W25Q128_CS(1);wait_busy();}
最终配置:
把文件合并,接线合并,初始化后在Main函数更改写法让读写操作显示在屏幕上:
#include "sys.h"
#include "uart1.h"
#include "delay.h"
#include "led.h"
#include "SPI.h"
#include "w25q128.h"
#include "oled.h"
#include "IIC.h"
#include "string.h"uint8_t data_write[4] = {0xAA, 0xBB, 0xCC, 0xDD};
uint8_t data_read[4] = {0};
int main(void)
{HAL_Init(); stm32_clock_init(RCC_PLL_MUL9); SPI_INIT();uart1_init(115200); OLED_INIT();Oled_clear(); Oled_show_string(1,1,"HELLO WKX",24); delay_ms(1000); Oled_clear();char show[8];uint16_t number = w25q128_config();sprintf(show,"device id :%X",number);Oled_show_string(1,1,"Loading...",24); delay_ms(1000); Oled_clear();Oled_show_string(1,1,show,16);delay_ms(1000);Oled_clear(); write_page_data(0x000000, data_write, 4);Oled_show_string(1,1,"Writed succes",16);delay_ms(500);Oled_clear();Oled_show_string(1,1,"Reading data",16);delay_ms(500);Oled_clear();read_data(0x000000, data_read, 4);memset(show,0,8);sprintf(show,"%X",data_read[0]);Oled_show_string(1,1,show,16);sprintf(show,"%X",data_read[1]);Oled_show_string(40,1,show,16);sprintf(show,"%X",data_read[2]);Oled_show_string(80,1,show,16);while(1){ }
}
好了给看看效果:
OLED&W25&Q128
祝你看完就会
相关文章:

STM32IIC与SPI详解
单片机里的通信协议其实蛮多的,IIC;SPI;MQTT;CAN;包括串口也是一种通信协议。而串口通信虽然实现了全双工,但需要至少三根线,为了节省这一根线的成本,于是IIC诞生了。 目录 一.IIC…...

K8s第三节:k8s1.23.1升级为k8s1.30.0
上回书说到我们使用了kubeadm安装了k8s1.23.1,但是在k8s1.24之前还是使用docker作为容器运行时,所以这一节我打算将我安装的k8s集群升级为1.30.0版本; 1、修改containerd 配置 因为我们安装的docker自带containerd,所以我们不需要重新安装con…...
.gitignore不生效的解决方案
为什么会不生效 因为文件已经被git追踪(或者说被track 或者说被索引,都是一个意思)。 目前.gitignore面对已经被git追踪的文件是无法生效的。(这是现状,我们只能接收这个现状。不过个人觉得git官方可以对这方面进行优化调整,让其…...

脱胎于 S 语言的R语言,Ross Ihaka 和 Robert Gentleman 和社区的力量让 R 在学术界与研究机构放光彩
R语言从一门用于统计学教学的编程语言,发展成为全球数据科学领域的重要工具,离不开其强大的功能、丰富的社区资源和开源精神。这些都离不开Ross Ihaka 和 Robert Gentleman 和 社区的力量。 在1990年代初,新西兰奥克兰大学的统计学教授Ross I…...

JavaEE 第6节 内存可见性问题以及解决方法
目录 一、什么是内存可见性问题? 1、问题代码演示 2、基础知识铺垫 1)硬件层面 2)模型层面(JMM) 二、内存可见性问题的原因 三、volatile解决内存可见性问题 一、什么是内存可见性问题? 1、问题代码…...
es基本操作
以下是一些 Elasticsearch 常用的命令,涵盖了索引管理、数据操作和集群管理等方面: 基本操作 检查集群状态: curl -X GET "localhost:9200/_cluster/health?pretty"查看集群健康状态和基本信息。 查看所有索引: curl…...

开源 AI 智能名片 S2B2C 商城小程序赋能下的社区团购商业模式研究
摘要:本文深入探讨了社区团购商业模式的本质、特点及其优势,并详细分析了开源 AI 智能名片 S2B2C 商城小程序在社区团购中的应用与价值。通过对相关案例的研究和数据的分析,揭示了这一创新组合对社区商业生态的重要影响,为未来社区…...
AutoSar AP软件规范中CM介绍及功能概要
1. 前言 为了理解AutoSar AP中EM的概念,生搬硬套的翻译了《 AUTOSAR SWS CommunicationManagement.pdf》的介绍部分,并按照自己的理解进行了修改。如下 2. AUTOSAR_SWS_CommunicationManagement.pdf的介绍部分 本文件包含AUTOSAR AP通信管理的功能、A…...
【图形学】TA之路-向量
向量 向量 是一个有大小和方向的数学对象。在三维空间中,向量通常表示为 (v_x, v_y, v_z)。 基本操作 加法: a b (a_x b_x, a_y b_y, a_z b_z)减法: a - b (a_x - b_x, a_y - b_y, a_z - b_z)标量乘法: k * v (k * v_x, …...

[flink]部署模式
部署模式 在一些应用场景中,对于集群资源分配和占用的方式,可能会有特定的需求。 Flink为各种场景提供了不同的部署模式,主要有以下三种:会话模式(Session Mode)、单作业模式(Per-Job Mode&…...

为什么不用postman做自动化
面试的时候被问到:为什么不用postman做自动化 打开postman,看到用例集管理、API 管理、环境管理这三个功能,用户体验感算得上品牌等级了 为什么不用呢,文心一言给了一些答案 不适合大规模自动化测试:Postman 主要是为…...

一、Matlab基础
文章目录 一、Matlab界面二、Matlab窗口常用命令三、Matlab的数据类型3.1 数值类型3.2 字符和字符串3.3 逻辑类型3.4 函数句柄3.5 结构类型3.6 细胞数组 四、Matlab的运算符4.1 算术运算符4.2 关系运算符4.3 逻辑运算4.4 运算符优先级 五、Matlab的矩阵5.1 矩阵的建立5.2 矩阵的…...
执行java -jar命令,显示jar中没有主清单属性
在Java中,一个"主清单属性"(Main-Class attribute)是指定JAR文件中包含的应用程序入口点,即包含main方法的类的完全限定名。如果你尝试运行一个没有主清单属性的JAR文件,你可能会看到错误消息,如…...

【C++进阶】红黑树
目录 什么是红黑树?红黑树红黑树的性质 定义红黑树红黑树的操作insertinorderfindheightsize构造函数析构函数赋值拷贝判断红黑树 全部代码总结 什么是红黑树? 红黑树 红黑树(Red-Black Tree)是一种自平衡的二叉搜索树ÿ…...

linux使用ssh连接一直弹出密码框问题
1.查看ssh服务的状态 输入以下命令: sudo service sshd status 小编已经安装了。 如果出现 Loaded: error (Reason: No such file or directory) 提示的话,说名没有安装ssh服务,按照第二步:安装ssh服务。 如果出现 Active: in…...
Python 3 数据结构
Python 3 数据结构 引言 Python 是一种高级编程语言,因其简洁明了的语法和强大的功能而广受欢迎。在 Python 中,数据结构是组织和存储数据的方式,对于编写高效和可维护的代码至关重要。本文将深入探讨 Python 3 中的主要数据结构࿰…...

【开源社区】Elasticsearch(ES)中空值字段 null_value 及通过exists查找非空文档
文章目录 0、声明1、问题描述2、问题剖析2.1 NULL或者空值类型有哪些2.2 案例讲解:尝试检索值为 null 的字段2.3 解决思路 3、使用 null_value 的诸多坑(避免生产事故)3.1 null_value 替换的是索引,并不会直接替换源数据3.2 不支持…...

JavaDS —— 位图(BitSet)与 布隆过滤器
位图 引入问题:给40亿个不重复的无符号整数,没排过序。给一个无符号整数,如何快速判断一个数是否在这40亿个数中。 首先要注意 40 亿个数据如果使用 整型(int) 来存放的话,就是要 40 亿个整型,一个整型有…...
如何确保场外个股期权交易的安全?
如何确保场外个股期权交易的安全?投资者可以采取以下措施,以提高交易的安全性和减少风险: 增强知识储备:深入学习期权的基础知识,包括不同类型的期权、它们的权利和义务、定价方式以及风险特性,从而提升自…...

第2章:LabVIEW FPGA未来发展方向《LabVIEW ZYNQ FPGA宝典》
2.1:NI的LabVIEW FPGA未来战略部署 在展望NI公司的LabVIEW FPGA技术未来发展趋势之前,让我们先来回顾一下LabVIEW与FPGA的技术发展历程,如图2-1所示。可以看出,NI公司的LabVIEW FPGA软件一方面是跟随Xilinx最新的FPGA硬件可持续发…...
Python|GIF 解析与构建(5):手搓截屏和帧率控制
目录 Python|GIF 解析与构建(5):手搓截屏和帧率控制 一、引言 二、技术实现:手搓截屏模块 2.1 核心原理 2.2 代码解析:ScreenshotData类 2.2.1 截图函数:capture_screen 三、技术实现&…...

观成科技:隐蔽隧道工具Ligolo-ng加密流量分析
1.工具介绍 Ligolo-ng是一款由go编写的高效隧道工具,该工具基于TUN接口实现其功能,利用反向TCP/TLS连接建立一条隐蔽的通信信道,支持使用Let’s Encrypt自动生成证书。Ligolo-ng的通信隐蔽性体现在其支持多种连接方式,适应复杂网…...

JavaSec-RCE
简介 RCE(Remote Code Execution),可以分为:命令注入(Command Injection)、代码注入(Code Injection) 代码注入 1.漏洞场景:Groovy代码注入 Groovy是一种基于JVM的动态语言,语法简洁,支持闭包、动态类型和Java互操作性,…...

手游刚开服就被攻击怎么办?如何防御DDoS?
开服初期是手游最脆弱的阶段,极易成为DDoS攻击的目标。一旦遭遇攻击,可能导致服务器瘫痪、玩家流失,甚至造成巨大经济损失。本文为开发者提供一套简洁有效的应急与防御方案,帮助快速应对并构建长期防护体系。 一、遭遇攻击的紧急应…...

盘古信息PCB行业解决方案:以全域场景重构,激活智造新未来
一、破局:PCB行业的时代之问 在数字经济蓬勃发展的浪潮中,PCB(印制电路板)作为 “电子产品之母”,其重要性愈发凸显。随着 5G、人工智能等新兴技术的加速渗透,PCB行业面临着前所未有的挑战与机遇。产品迭代…...

Vue3 + Element Plus + TypeScript中el-transfer穿梭框组件使用详解及示例
使用详解 Element Plus 的 el-transfer 组件是一个强大的穿梭框组件,常用于在两个集合之间进行数据转移,如权限分配、数据选择等场景。下面我将详细介绍其用法并提供一个完整示例。 核心特性与用法 基本属性 v-model:绑定右侧列表的值&…...
java 实现excel文件转pdf | 无水印 | 无限制
文章目录 目录 文章目录 前言 1.项目远程仓库配置 2.pom文件引入相关依赖 3.代码破解 二、Excel转PDF 1.代码实现 2.Aspose.License.xml 授权文件 总结 前言 java处理excel转pdf一直没找到什么好用的免费jar包工具,自己手写的难度,恐怕高级程序员花费一年的事件,也…...

蓝牙 BLE 扫描面试题大全(2):进阶面试题与实战演练
前文覆盖了 BLE 扫描的基础概念与经典问题蓝牙 BLE 扫描面试题大全(1):从基础到实战的深度解析-CSDN博客,但实际面试中,企业更关注候选人对复杂场景的应对能力(如多设备并发扫描、低功耗与高发现率的平衡)和前沿技术的…...
鱼香ros docker配置镜像报错:https://registry-1.docker.io/v2/
使用鱼香ros一件安装docker时的https://registry-1.docker.io/v2/问题 一键安装指令 wget http://fishros.com/install -O fishros && . fishros出现问题:docker pull 失败 网络不同,需要使用镜像源 按照如下步骤操作 sudo vi /etc/docker/dae…...
Java求职者面试指南:计算机基础与源码原理深度解析
Java求职者面试指南:计算机基础与源码原理深度解析 第一轮提问:基础概念问题 1. 请解释什么是进程和线程的区别? 面试官:进程是程序的一次执行过程,是系统进行资源分配和调度的基本单位;而线程是进程中的…...