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

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详解

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

K8s第三节:k8s1.23.1升级为k8s1.30.0

上回书说到我们使用了kubeadm安装了k8s1.23.1,但是在k8s1.24之前还是使用docker作为容器运行时&#xff0c;所以这一节我打算将我安装的k8s集群升级为1.30.0版本&#xff1b; 1、修改containerd 配置 因为我们安装的docker自带containerd&#xff0c;所以我们不需要重新安装con…...

.gitignore不生效的解决方案

为什么会不生效 因为文件已经被git追踪(或者说被track 或者说被索引&#xff0c;都是一个意思)。 目前.gitignore面对已经被git追踪的文件是无法生效的。&#xff08;这是现状&#xff0c;我们只能接收这个现状。不过个人觉得git官方可以对这方面进行优化调整&#xff0c;让其…...

脱胎于 S 语言的R语言,Ross Ihaka 和 Robert Gentleman 和社区的力量让 R 在学术界与研究机构放光彩

R语言从一门用于统计学教学的编程语言&#xff0c;发展成为全球数据科学领域的重要工具&#xff0c;离不开其强大的功能、丰富的社区资源和开源精神。这些都离不开Ross Ihaka 和 Robert Gentleman 和 社区的力量。 在1990年代初&#xff0c;新西兰奥克兰大学的统计学教授Ross I…...

JavaEE 第6节 内存可见性问题以及解决方法

目录 一、什么是内存可见性问题&#xff1f; 1、问题代码演示 2、基础知识铺垫 1&#xff09;硬件层面 2&#xff09;模型层面&#xff08;JMM&#xff09; 二、内存可见性问题的原因 三、volatile解决内存可见性问题 一、什么是内存可见性问题&#xff1f; 1、问题代码…...

es基本操作

以下是一些 Elasticsearch 常用的命令&#xff0c;涵盖了索引管理、数据操作和集群管理等方面&#xff1a; 基本操作 检查集群状态&#xff1a; curl -X GET "localhost:9200/_cluster/health?pretty"查看集群健康状态和基本信息。 查看所有索引&#xff1a; curl…...

开源 AI 智能名片 S2B2C 商城小程序赋能下的社区团购商业模式研究

摘要&#xff1a;本文深入探讨了社区团购商业模式的本质、特点及其优势&#xff0c;并详细分析了开源 AI 智能名片 S2B2C 商城小程序在社区团购中的应用与价值。通过对相关案例的研究和数据的分析&#xff0c;揭示了这一创新组合对社区商业生态的重要影响&#xff0c;为未来社区…...

AutoSar AP软件规范中CM介绍及功能概要

1. 前言 为了理解AutoSar AP中EM的概念&#xff0c;生搬硬套的翻译了《 AUTOSAR SWS CommunicationManagement.pdf》的介绍部分&#xff0c;并按照自己的理解进行了修改。如下 2. AUTOSAR_SWS_CommunicationManagement.pdf的介绍部分 本文件包含AUTOSAR AP通信管理的功能、A…...

【图形学】TA之路-向量

向量 向量 是一个有大小和方向的数学对象。在三维空间中&#xff0c;向量通常表示为 (v_x, v_y, v_z)。 基本操作 加法&#xff1a; a b (a_x b_x, a_y b_y, a_z b_z)减法&#xff1a; a - b (a_x - b_x, a_y - b_y, a_z - b_z)标量乘法&#xff1a; k * v (k * v_x, …...

[flink]部署模式

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

为什么不用postman做自动化

面试的时候被问到&#xff1a;为什么不用postman做自动化 打开postman&#xff0c;看到用例集管理、API 管理、环境管理这三个功能&#xff0c;用户体验感算得上品牌等级了 为什么不用呢&#xff0c;文心一言给了一些答案 不适合大规模自动化测试&#xff1a;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中&#xff0c;一个"主清单属性"&#xff08;Main-Class attribute&#xff09;是指定JAR文件中包含的应用程序入口点&#xff0c;即包含main方法的类的完全限定名。如果你尝试运行一个没有主清单属性的JAR文件&#xff0c;你可能会看到错误消息&#xff0c;如…...

【C++进阶】红黑树

目录 什么是红黑树&#xff1f;红黑树红黑树的性质 定义红黑树红黑树的操作insertinorderfindheightsize构造函数析构函数赋值拷贝判断红黑树 全部代码总结 什么是红黑树&#xff1f; 红黑树 红黑树&#xff08;Red-Black Tree&#xff09;是一种自平衡的二叉搜索树&#xff…...

linux使用ssh连接一直弹出密码框问题

1.查看ssh服务的状态 输入以下命令&#xff1a; sudo service sshd status 小编已经安装了。 如果出现 Loaded: error (Reason: No such file or directory) 提示的话&#xff0c;说名没有安装ssh服务&#xff0c;按照第二步&#xff1a;安装ssh服务。 如果出现 Active: in…...

Python 3 数据结构

Python 3 数据结构 引言 Python 是一种高级编程语言&#xff0c;因其简洁明了的语法和强大的功能而广受欢迎。在 Python 中&#xff0c;数据结构是组织和存储数据的方式&#xff0c;对于编写高效和可维护的代码至关重要。本文将深入探讨 Python 3 中的主要数据结构&#xff0…...

【开源社区】Elasticsearch(ES)中空值字段 null_value 及通过exists查找非空文档

文章目录 0、声明1、问题描述2、问题剖析2.1 NULL或者空值类型有哪些2.2 案例讲解&#xff1a;尝试检索值为 null 的字段2.3 解决思路 3、使用 null_value 的诸多坑&#xff08;避免生产事故&#xff09;3.1 null_value 替换的是索引&#xff0c;并不会直接替换源数据3.2 不支持…...

JavaDS —— 位图(BitSet)与 布隆过滤器

位图 引入问题&#xff1a;给40亿个不重复的无符号整数&#xff0c;没排过序。给一个无符号整数&#xff0c;如何快速判断一个数是否在这40亿个数中。 首先要注意 40 亿个数据如果使用 整型&#xff08;int) 来存放的话&#xff0c;就是要 40 亿个整型&#xff0c;一个整型有…...

如何确保场外个股期权交易的安全?

如何确保场外个股期权交易的安全&#xff1f;投资者可以采取以下措施&#xff0c;以提高交易的安全性和减少风险&#xff1a; 增强知识储备&#xff1a;深入学习期权的基础知识&#xff0c;包括不同类型的期权、它们的权利和义务、定价方式以及风险特性&#xff0c;从而提升自…...

第2章:LabVIEW FPGA未来发展方向《LabVIEW ZYNQ FPGA宝典》

2.1&#xff1a;NI的LabVIEW FPGA未来战略部署 在展望NI公司的LabVIEW FPGA技术未来发展趋势之前&#xff0c;让我们先来回顾一下LabVIEW与FPGA的技术发展历程&#xff0c;如图2-1所示。可以看出&#xff0c;NI公司的LabVIEW FPGA软件一方面是跟随Xilinx最新的FPGA硬件可持续发…...

Linux应用开发之网络套接字编程(实例篇)

服务端与客户端单连接 服务端代码 #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <arpa/inet.h> #include <pthread.h> …...

工业安全零事故的智能守护者:一体化AI智能安防平台

前言&#xff1a; 通过AI视觉技术&#xff0c;为船厂提供全面的安全监控解决方案&#xff0c;涵盖交通违规检测、起重机轨道安全、非法入侵检测、盗窃防范、安全规范执行监控等多个方面&#xff0c;能够实现对应负责人反馈机制&#xff0c;并最终实现数据的统计报表。提升船厂…...

练习(含atoi的模拟实现,自定义类型等练习)

一、结构体大小的计算及位段 &#xff08;结构体大小计算及位段 详解请看&#xff1a;自定义类型&#xff1a;结构体进阶-CSDN博客&#xff09; 1.在32位系统环境&#xff0c;编译选项为4字节对齐&#xff0c;那么sizeof(A)和sizeof(B)是多少&#xff1f; #pragma pack(4)st…...

AtCoder 第409​场初级竞赛 A~E题解

A Conflict 【题目链接】 原题链接&#xff1a;A - Conflict 【考点】 枚举 【题目大意】 找到是否有两人都想要的物品。 【解析】 遍历两端字符串&#xff0c;只有在同时为 o 时输出 Yes 并结束程序&#xff0c;否则输出 No。 【难度】 GESP三级 【代码参考】 #i…...

Go 语言接口详解

Go 语言接口详解 核心概念 接口定义 在 Go 语言中&#xff0c;接口是一种抽象类型&#xff0c;它定义了一组方法的集合&#xff1a; // 定义接口 type Shape interface {Area() float64Perimeter() float64 } 接口实现 Go 接口的实现是隐式的&#xff1a; // 矩形结构体…...

1688商品列表API与其他数据源的对接思路

将1688商品列表API与其他数据源对接时&#xff0c;需结合业务场景设计数据流转链路&#xff0c;重点关注数据格式兼容性、接口调用频率控制及数据一致性维护。以下是具体对接思路及关键技术点&#xff1a; 一、核心对接场景与目标 商品数据同步 场景&#xff1a;将1688商品信息…...

JVM垃圾回收机制全解析

Java虚拟机&#xff08;JVM&#xff09;中的垃圾收集器&#xff08;Garbage Collector&#xff0c;简称GC&#xff09;是用于自动管理内存的机制。它负责识别和清除不再被程序使用的对象&#xff0c;从而释放内存空间&#xff0c;避免内存泄漏和内存溢出等问题。垃圾收集器在Ja…...

关键领域软件测试的突围之路:如何破解安全与效率的平衡难题

在数字化浪潮席卷全球的今天&#xff0c;软件系统已成为国家关键领域的核心战斗力。不同于普通商业软件&#xff0c;这些承载着国家安全使命的软件系统面临着前所未有的质量挑战——如何在确保绝对安全的前提下&#xff0c;实现高效测试与快速迭代&#xff1f;这一命题正考验着…...

视频行为标注工具BehaviLabel(源码+使用介绍+Windows.Exe版本)

前言&#xff1a; 最近在做行为检测相关的模型&#xff0c;用的是时空图卷积网络&#xff08;STGCN&#xff09;&#xff0c;但原有kinetic-400数据集数据质量较低&#xff0c;需要进行细粒度的标注&#xff0c;同时粗略搜了下已有开源工具基本都集中于图像分割这块&#xff0c…...

基于TurtleBot3在Gazebo地图实现机器人远程控制

1. TurtleBot3环境配置 # 下载TurtleBot3核心包 mkdir -p ~/catkin_ws/src cd ~/catkin_ws/src git clone -b noetic-devel https://github.com/ROBOTIS-GIT/turtlebot3.git git clone -b noetic https://github.com/ROBOTIS-GIT/turtlebot3_msgs.git git clone -b noetic-dev…...