首页 > 其他分享 >一文讲清,常用通信协议IIC,SPI,串口,基于STM32

一文讲清,常用通信协议IIC,SPI,串口,基于STM32

时间:2024-09-09 09:56:18浏览次数:7  
标签:SCL 通信协议 SPI Init SDA IIC 串口 I2C

目录

一、通讯的基本概念

1.串行通讯

2.并行通讯

3.传输模式(单工、半双工、全双工)

二、常见通讯协议(串口、IIC、SPI)

1.串口

(1)UART和USART的区别是什么?

(2)UART(TTL、RS232、RS485)

(3)基于STM32的HAL库的串口配置

2.IIC

(1)物理层

(2)协议层

(3)软件模拟IIC通讯代码

(4)有关IIC面试的问题

(5)硬件IIC和软件IIC

3.SPI

(1)物理层

(2)协议层

(3)基于STM32的HAL库如何配置


一、通讯的基本概念

1.串行通讯

逐位传输,传输线少,适合远距离传输,抗干扰能力强,传输速率,可以分为:同步串行,异步串行

2.并行通讯

多位数据同时传输,传输速度快,传输线多,适合距离传输,抗干扰能力强

3.传输模式(单工、半双工、全双工)

二、常见通讯协议(串口、IIC、SPI)

1.串口

串口通讯是一种全双工的串行通讯方式,主要可以分为UART(universal asynchronous receiver and transmitter通用异步收/发器)和USART(universal synchronous asynchronous receiver and transmitter通用同步/异步/收/发器)

(1)UART和USART的区别是什么?

从名字上看USART比UART增加了同步通信的功能,USART多提供了主动时钟。

后续主要讨论UART(串口异步时钟通讯协议),后续将UART统一简称为串口

(2)UART(TTL、RS232、RS485)

①按照电平标准区分:

TTL电平:3.3/5V表示1,0V表示0V表示0

RS232:-3~-15V表示1,+3~+15V

RS485:两线压差+2~+6表示1,-2~-6表示0(差分信号)

在实际应用中一般只使用到TXD,RXD,GND三根信号线

②协议层

串口发送一个字节(8位)数据的格式,如下图所示,分为有校验位无校验位,每一个字节都装载在一个数据帧中间,每个数据帧由起始位、数据位、停止位三个部分组成。

无校验位

有校验位

③串口参数

波特率:串口的通信速率(每秒钟传送二进制数据的位数),bit/s(bps)为单位,常用的波特率有:9600、19200、38400、57600和115200,如果波特率是115200,则代表每一位数据在线上持续的时间为1/115200=8.68us

起始位:一个数据帧的开始,固定为低电平。不传输数据时, UART 数据传输线通常保持高电平。开始数据传输,发送UART 会将传输线电压从高拉低,并保持1 位时间。

停止位:一个数据帧的结束,固定为高电平。为了表示数据包结束,发送 UART 将数据传输线电压从低拉高并保持1 到 2 时间。

④校验位

在有效数据之后,有一个可选的数据校验位。由于数据通信相对更容易受到外部干扰导致传输数据出现偏差,可以在传输过程加上校验位来解决这个问题。校验方法有奇校验(odd)、偶校验(even)、 0 校验(space)、 1 校验(mark)以及无校验(noparity)。

奇偶校验:奇校验表示数据中“1”的个数与校验位“1”的个数之和为奇数;偶校验表示数据中“1”的个数与校验位“1”的个数之和为偶数。
代码和校验:发送方将所发数据块求和,产生一个字节的校验字符附加到数据块末尾。接收方采用同样方式进行检测
循环冗余校验:通过某种数学运算实现有效信息与校验位之间的循环校验,常用于磁盘信息的传输、存储区的完整性校验等。

(3)基于STM32的HAL库的串口配置
// UART1 初始化函数
void MX_USART1_UART_Init(void) {
    // 配置USART1句柄的实例和初始化参数
    huart1.Instance = USART1;                // 选择USART1外设
    huart1.Init.BaudRate = 9600;             // 设置波特率为9600
    huart1.Init.WordLength = UART_WORDLENGTH_8B; // 设置数据位为8位
    huart1.Init.StopBits = UART_STOPBITS_1; // 设置停止位为1位
    huart1.Init.Parity = UART_PARITY_NONE;   // 设置无奇偶校验
    huart1.Init.Mode = UART_MODE_TX_RX;      // 使能发送和接收模式
    huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE; // 不使用硬件流控制
    huart1.Init.OverSampling = UART_OVERSAMPLING_16; // 设置过采样为16
//过采样率为16的意思为每个比特会被采样 16 次。在每个比特周期内,这些采样值会被用来判断该比特的实际值。
   
}

2.IIC

IIC(Inter-Integrated Circuit)是一种2线式同步半双工串行通信方式,用以连接控制器和气周围设备,多用于主从之间的通信。适用于小数据量和短距离的场合。

(1)物理层

IIC一共只有两个总线,一条是双向的串行数据线SDA(用来传输数据),一条是串行时钟线SCL(控制数据发送的时序的)。

主从设备的区分:控制时钟线(即控制SCL的电平高低变换)的就是主设备。

为了避免总线信号的混乱,设备连接到总线的输出端时必须是漏极开路(OD)输出或集电极开路(OC)输出,其可独立输入输出低电平和高阻状态,若要产生高电平,则需要外部上拉电阻,空闲设备被拉到了高阻态,就相当于断路,其他开启了的设备正常通信。

(2)协议层

IIC总线在传送数据的过程中总共有三种类型信号:分别是:开始信号,结束信号,应答信号。如下图所示:

开始信号(START):当SCL为高电平,SDA由高变低,则开始传送数据

结束信号(STOP):当SCL为高电平,SDA由低变高,则结束传送数据

数据位传送(DATA):在SCL保持高电平期间,SDA上的电平保持稳定,低电平为数据0、高电平为数据1

应答信号(ACK):主机SCL拉高,读取从机SDA的电平,若为低电平表示应答

非应答信号(NACK):主机SCL拉高,读取从机SDA的电平,若为高电平表示非应答

应答信号出现在一个字节(8bit)传输后的第9位,0为应答,1为非应答

起始信号:

//起始信号
void IIC_Start(void)
{
	SDA_H;						//拉高SDA线
	SCL_H;						//拉高SCL线
	DelayUs(iicInfo.speed);		//延时,速度控制
	
	SDA_L;						//当SCL线为高时,SDA线一个下降沿代表开始信号
	DelayUs(iicInfo.speed);		//延时,速度控制
	SCL_L;						//钳住SCL线,以便发送数据
}  

停止信号

//停止信号
void IIC_Stop(void)
{
	SDA_L;						//拉低SDA线
	SCL_L;						//拉低SCL先
	DelayUs(iicInfo.speed);		//延时,速度控制
	
	SCL_H;						//拉高SCL线
	SDA_H;						//拉高SDA线,当SCL线为高时,SDA线一个上升沿代表停止信号
	DelayUs(iicInfo.speed);
}

应答信号

//应答信号
//产生ACK应答,读取从机一字节数据后还要接着读的时候使用
//SCL在SDA一直为低电平期间完成低高电平转换
void IIC_Ack(void)
{	
	SCL_L;						//拉低SCL线
	SDA_L;						//拉低SDA线<----
	DelayUs(iicInfo.speed);
	SCL_H;						//拉高SCL线
	DelayUs(iicInfo.speed);
	SCL_L;						//拉低SCL线
}
//不产生ACK应答,读取从机一字节数据后不读了的时候使用    
//SCL在SDA一直为高电平期间完成低高电平转换
void IIC_NAck(void)
{
	SCL_L;						//拉低SCL线
	SDA_H;						//拉高SDA线<----
	DelayUs(iicInfo.speed);
	SCL_H;						//拉高SCL线
	DelayUs(iicInfo.speed);
	SCL_L;						//拉低SCL线
}
//等待ACK应答    
//发送完一个字节后(释放SDA)的下一个时钟高电平时期,读取SDA电平,0为收到应答
_Bool IIC_WaitAck(unsigned int timeOut)
{
	SDA_H;DelayUs(iicInfo.speed);			//拉高SDA线
	SCL_H;DelayUs(iicInfo.speed);			//拉高SCL线
	
	while(SDA_R)							//如果读到SDA线为1,则等待。应答信号应是0
	{
		if(--timeOut)
		{
			printf("WaitAck TimeOut\r\n");
			IIC_Stop();						//超时未收到应答,则停止总线			
			return IIC_Err;					//返回失败
		}		
		DelayUs(iicInfo.speed);
	}	
	SCL_L;									//拉低SCL线,以便继续收发数据	
	return IIC_OK;							//返回成功	
}

发送数据

void IIC_Send(unsigned char byte)
{
	unsigned char count = 0;
	
    SCL_L;							//拉低时钟开始数据传输	
    for(; count < 8; count++)		//循环8次,每次发送一个bit
    {
		if(byte & 0x80)				//【先发送最高位,大端传输】
			SDA_H;
		else
			SDA_L;
		
		byte <<= 1;					//byte左移1位
		
		DelayUs(iicInfo.speed);
		SCL_H;
		DelayUs(iicInfo.speed);
		SCL_L;
    }
}   

接收数据

unsigned char IIC_Read(void)
{	
	unsigned char count = 0, receive = 0;	
	SDA_H;						//拉高SDA线,开漏状态下,需线拉高以便读取数据
	
    for(; count < 8; count++ )	//循环8次,每次发送一个bit
	{
		SCL_L;                  //拉低,从机放数据
		DelayUs(iicInfo.speed);
		SCL_H;                  //拉高,主机读数据
		
        receive <<= 1;			//左移一位	
        if(SDA_R)				//如果SDA线为1,则receive变量自增,每次自增都是对bit0的+1,然后下一次循环会先左移一次
			receive++;		
		DelayUs(iicInfo.speed);
    }	
    return receive;	
}
(3)软件模拟IIC通讯代码

①写数据

  • 字节写

void IIC_SEND_BYTE(u8 slaveaddr,u8 registeraddr,u8 byte);//发送到具体从机地址及相关寄存器一个字节
{
	IIC_Start();           //通讯开始
	IIC_Send(slaveaddr);   //先发送从机的地址,寻址从机
	IIC_WaitAck();         //得到回应,说明,电路中有这个外设器件
	IIC_Send(registeraddr);//寻址这个器件中的相关寄存器
	IIC_WaitAck();         //得到回应,说明这个寄存器存在
	IIC_Send(byte);        //发送一个字节
	IIC_WaitAck();         //等待回应
}
  • 页写

void IIC_SEND_BYTES(u8 slaveaddr,u8 registeraddr,u8 *pbuffer,u16 num );//发送一串数据给具体从机的相关寄存器
{
	IIC_Start();             //开始通讯
	IIC_Send(slaveaddr);     //寻址从机
	IIC_WaitAck();           //等待回应
	IIC_Send(registeraddr);  //寻址寄存器
	IIC_WaitAck();           //等待回应

	for(t=0;t<num;t++)       //发送数组中的数据
	{
		IIC_Send(*(pbuffer+t));
		IIC_WaitAck();       //每发送一个字节,都需要一个应答
	}
	IIC_Stop();              //终止通讯
}

②读数据

void I2C_READ_BYTES(u8 slaveaddr,u8 registeraddr,u8 *pbuffer,u16 num );//读取具体从机的相关寄存器一串字节
{
    I2C_Start();           //开始通讯
	I2C_Send(slaveaddr);   //寻址从机
	I2C_WaitAck();         //等待回应
	I2C_Send(registeraddr);//寻址寄存器
	I2C_WaitAck();         //等待回应

	I2C_Start();           //重新通讯
	I2C_Send(slaveaddr+1); //改为读数据
	I2C_WaitAck();         //等待回应

	for(t=0;t<num;t++)     //存储数据
	{
		*(pbuffer+t)=I2C_Read();
		if (t== num-1)     //字节没发完,必须给出应答,发完的给个非应答信号
	 	{
	   	 	IIC_Ack();
		}
		else
		{
	    	IIC_NAck();
		}
	}
	IIC_Stop();            //通讯结束
}
(4)有关IIC面试的问题

①IIC总线的仲裁了解多少?

I2C总线具有多主控能力,有时会发生两个或多个主器件同时想占用总线的情况,这种情况叫做总线竞争

I2C可以对发生在SDA线上的总线竞争进行仲裁,I2C总线的仲裁逻辑是建立在线与功能上的,有一个拉低SDA总线就是低

仲裁原则是这样的:每一个主器件每次发送一位数据,然后比较总线上所呈现的电平与自己所发送的数据是否一致,如果一致,继续发送下一位数据,否则就退出竞争。

总线竞争的仲裁在两个层次上进行:首先是地址位的比较,如果主器件寻址同一个从器件,则进入数据位比较,从而确保竞争仲裁的可靠性。由于是利用IIC总线上的信息进行仲裁,不会造成信息的丢失。

②I2C时钟信号(SCL)的同步问题

在I2C总线上传送信息时的时钟同步信号是由挂接在SCL线上的所有器件的逻辑“与”完成的。SCL低电平时间由时钟低电平期最长的器件确定,而时钟高电平时间由时钟高电平期最短的器件确定

③当通过IIC配置外设失败时,可以从以下几种角度出发进行排查:

  1. 硬件连接问题:检查IIC总线的物理连接是否正确。确保SDA(数据线)和SCL(时钟线)正确连接到外设和主控制器,并且没有短路或断开的情况。还要确保上拉电阻的数值和位置正确。
  2. 电源供电问题:确认外设和主控制器都正常供电,并检查电源电压是否在可接受范围内。低电压可能导致通信错误或不稳定。
  3. 地址设置问题:检查外设的地址设置是否正确。确保外设的IIC地址与要求的地址匹配。有些外设具有可编程地址,因此需要验证是否正确设置了地址。
  4. IIC协议参数设置问题:确保主控制器和外设之间的IIC协议参数设置一致,如速率、传输模式等。确认主控制器发送的start bit、stop bit等控制信号正确地驱动外设。
  5. 软件程序问题:检查软件代码是否正确实现了IIC通信协议。确认发送和接收的数据格式、顺序、时序是否符合规范。还要确保正确处理ACK(应答)信号和超时等异常情况。
  6. 外设工作状态问题:检查外设自身是否正常工作。对于某些外设,可能需要进行初始化或配置才能正常通信。确保外设处于正确的模式或状态
(5)硬件IIC和软件IIC

软件IIC:软件IIC通信指的是用单片机的两个I/O端口模拟出来的IIC,用软件控制管脚状态以模拟I2C通信波形,软件模拟寄存器的工作方式。软件iic一般可以方便移植到任何设备之下,则可按照上述的代码进行设计

硬件IIC:一块硬件电路,硬件I2C对应芯片上的I2C外设,有相应I2C驱动电路,其所使用的I2C管脚也是专用的,硬件(固件)I2C是直接调用内部寄存器进行配置。基于STM32的硬件IIC的配置如下:

// I2C 外设的初始化函数
void MX_I2C1_Init(void)
{
    hi2c1.Instance = I2C1; // 选择 I2C1 外设实例
    hi2c1.Init.ClockSpeed = 100000; // 设置 I2C 时钟频率为 100 kHz
    hi2c1.Init.DutyCycle = I2C_DUTYCYCLE_2; // 设置占空比为 2 (标准模式下)
    hi2c1.Init.OwnAddress1 = 0; // 本设备地址设置为 0(此设备为主机,不使用地址)
    hi2c1.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT; // 设置地址模式为 7 位
    hi2c1.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE; // 禁用双地址模式
    hi2c1.Init.OwnAddress2 = 0; // 双地址模式下的第二个地址设置为 0
    hi2c1.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE; // 禁用一般调用模式
    hi2c1.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE; // 禁用不拉伸模式
    HAL_I2C_Init(&hi2c1); // 初始化 I2C 外设并应用配置
}
3.SPI

串行外设接口(Serial Peripheral Interface,SPI)是一种高速、全双工、同步通信的串行接口。

(1)物理层

MISO ( Master Input Slave Output ) : 主设备数据输入,从设备数据输出;
MOSI ( Master Output Slave Input ) : 主设备数据输出,从设备数据输入;
SCLK ( Serial Clock ) : 时钟信号,由主设备产生;
CS/SS ( Chip Select/Slave Select ) : 从设备片选信号,由主设备控制,通常低电平有效

(2)协议层

①通讯的起始和停止信号

CS/SS信号由高变低,是SPI通讯的起始信号,CS/SS信号由低变高,是SPI通讯的停止信号

②数据有效性

在SCK信号的同步下,一个时钟周期内。数据的输入输出同时进行,如上图所示,在SCK上升沿,MOSI和MISO信号变化输出,在SCK下降沿, MOSI 及 MISO 的数据有效,高电平时表示数据“1”,为低电平时表示数据“0”,在其它时刻,数据无效。

③CPOL/CPHA 及通讯模式

SPI 一共有四种通讯模式,主要区别为:总线空闲时 SCK 的时钟状态数据采样时刻

时钟极性 CPOL :是指 SPI 通讯设备处于空闲状态时, CPOL=0 时, SCK 在空闲状态时为低电平,CPOL=1 时,SCK 在空闲状态时为高电平

时钟相位 CPHA :是指数据的采样的时刻,当 CPHA=0 时, MOSI 或 MISO 数据线上的信号将会在 SCK 时钟线的“奇数边沿被采样。当 CPHA=1 时,数据线在 SCK 的“偶数边沿”采样。

(3)基于STM32的HAL库如何配置
void SPI1_Init(void)
{
    // SPI1 外设初始化结构体
    hspi1.Instance = SPI1; // 选择 SPI1 外设
    hspi1.Init.Mode = SPI_MODE_MASTER; // 设为主模式
    hspi1.Init.Direction = SPI_DIRECTION_2LINES; // 双线单工模式(全双工)
    hspi1.Init.DataSize = SPI_DATASIZE_8BIT; // 8 位数据宽度
    hspi1.Init.CLKPolarity = SPI_POLARITY_LOW; // 时钟极性低
    hspi1.Init.CLKPhase = SPI_PHASE_1EDGE; // 时钟相位在第一个边沿采样
    hspi1.Init.NSS = SPI_NSS_SOFT; // 软件控制 NSS
    hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_16; // 波特率分频系数
    hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB; // 数据位先传送最高有效位
    hspi1.Init.TIMode = SPI_TIMODE_DISABLE; // 禁用 TI 模式
    hspi1.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE; // 禁用 CRC 校验
    hspi1.Init.CRCPolynomial = 10; // CRC 多项式,未启用时设置为任意值
}


参考以下内容:

常见的通讯协议总结(USART、IIC、SPI、485、CAN)-CSDN博客

STM32——关于USART的讲解与应用(一)(看完这篇你就懂了)-CSDN博客

IIC原理超详细讲解---值得一看-CSDN博客

【接口协议】04.IIC - 牛客网 (nowcoder.com)

SPI协议详解(图文并茂+超详细)-CSDN博客

HAL库STM32常用外设教程(八)—— SPI (读写W25Q128)_hal spi-CSDN博客

标签:SCL,通信协议,SPI,Init,SDA,IIC,串口,I2C
From: https://blog.csdn.net/weixin_53713973/article/details/141907155

相关文章

  • Spire.Doc for Java Version:12.9
    Spire.DocforJavaisaprofessionalWordAPIthatempowersJavaapplicationstocreate,convert,manipulateandprintWorddocumentswithoutdependencyonMicrosoftWord.Byusingthismultifunctionallibrary,developersareabletoprocesscopioustasks......
  • Spire.PDF for Java Version:10.9.0
    Spire.PDFforJavaisaPDFAPIthatenablesJavaapplicationstoread,writeandsavePDFdocumentswithoutusingAdobeAcrobat.UsingthisJavaPDFcomponent,developersandprogrammerscanimplementrichcapabilitiestocreatePDFfilesfromscratchor......
  • sping boot 基于 RESTful 风格,模拟增删改查操作
    RESTful-> 增:post 删:delete 改: put查: getRESTful资源路径,一般以s复数结尾 以下是代码示例:packagecom.example.springboot.controller;importorg.springframework.web.bind.annotation.*;@RestControllerpublicclassHello{@RequestMappi......
  • 天翼云存储SpinTires问题解析:d3dx9_43.dll文件丢失应对指南
    在使用天翼云存储或运行SpinTires等游戏时,有时会遇到系统提示“d3dx9_43.dll文件丢失”的错误。这个问题通常是由于DirectX组件中的d3dx9_43.dll文件未正确安装、损坏或丢失所导致的。以下是一些应对指南,帮助您解决这一问题:一、了解d3dx9_43.dll文件的重要性d3dx9_43.dll是D......
  • 事务发件箱模式在 .NET 云原生开发中的应用(基于Aspire)
    原文:TransactionalOutboxin.NETCloudNativeDevelopmentviaAspire作者:OleksiiNikiforov总览这篇文章提供了使用Aspire、DotNetCore.CAP、AzureServiceBus、AzureSQL、Bicep和azd实现Outbox模式的示例。源代码:https://github.com/NikiforovAll/cap-aspire......
  • Verilog:【8】基于FPGA实现SD NAND FLASH的SPI协议读写
    Verilog:【8】基于FPGA实现SDNANDFLASH的SPI协议读写在此介绍的是使用FPGA实现SDNANDFLASH的读写操作,以雷龙发展提供的CS创世SDNANDFLASH样品为例,分别讲解电路连接、读写时序与仿真和实验结果。目录1FLASH背景介绍2样品申请3电路结构与接口协议3.1SD......
  • 用串口来发送信号发生器的数据
    一、在串口模块的基础上加一点修改1.这一段讲做什么我的博客是连续剧,觉得有参考/纠错/鼓励的价值的话,我是非常期待能与大家多多交流的呀小梅哥的视频P14集,后半部分讲,能不能通过在大模块中操作一个控制信号send_go,进而影响子模块(就是串口模块,以下称串口模块)种的send_en信号,进而......
  • Dubbo源码剖析-SPI机制(超详细深度剖析篇)
    目录什么是SPISPI的工作原理SPI的作用SPI的缺点简单使用JDK的SPI与Dubbo的SPIDubbo为什么要使用SPI机制DubboSPI源码分析小结什么是SPI        SPI全称为ServiceProviderInterface,一种解耦接口和实现的手段,其实现原理是将接口的实现类全名称配置......
  • Spire.Office for Java 9.8.0 FIX
    独立Java库用于处理Office、PDF和条形码Spire.OfficeforJava是E-iceblue提供的企业级OfficeJavaAPI的组合,包括Spire.DocforJava、Spire.XLSforJava、Spire.PresentationforJava、Spire.PDFforJava和Spire.BarcodeforJava。开发人员可以使用Spire.Off......
  • 西门子s7通信协议
    西门子s7通信协议S7Comm(S7Communication)是西门子专有的协议,是西门子S7通讯协议簇里的一种。S7通信协议是西门子S7系列PLC内部集成的一种通信协议,是S7系列PLC的精髓所在。它是一种运行在传输层之上的(会话层/表示层/应用层)、经过特殊优化的通信协议,其信息传输可以基于MPI网络、......