【一文吃透】常见通信协议(SPI、IIC、UART、CAN)[面试重点]

目录

一、四种通信方式区别

二、SPI协议(全双工、速度快)

1.SPI特性

2.SPI工作流程

3.SPI四种工作模式

数据传输流程

三、IIC协议(半双工、速度中等)

1.IIC数据接口

2.IIC主要特点

3.工作流程介绍

(1)主机向从机写入数据过程

(2)主机读取从机数据过程

4.IIC常见问题

(1)为什么Open-Drain开漏输出需要上拉电阻

(2)为什么IIC需要漏极开路

(3)软件IIC和硬件IIC的区别

四、URAT协议(全双工、速度慢)

1.UART协议概念

2.UART的帧格式

(1)为什么UART的传输需要起始位?

(2)UART基本的数据形式

3.UART的波特率

(1)什么是波特率

(2)如何换算波特率

4.UART的缺点

(1)电气接口不统一

(2)抗干扰能力差

(3)通信距离极短

5.RS232标准

五、CAN通信(半双工)

1.CAN通信概念

2.CAN通信概念

3. CAN通信的应用领域

4. CAN帧格式与标识符

5. CAN通信模式

6. 使用HAL库实现CAN通信

6.1 STM32 CAN通信配置:

6.2 整体代码


一、四种通信方式区别

首先介绍一下四种通信方式的区别,在面试中也常见这些问题,方便大家理解记忆。

  1. SPI(Serial Peripheral Interface)是一种同步的串行通信协议,需要4根线:MISO、MOSI、SCLK和CS。SPI通信速度快,但只能在短距离内通信,且只能支持单主设备和多从设备的通信方式。
  2. IIC(Inter-Integrated Circuit)是一种同步的串行通信协议,需要2根线:SCL和SDA。IIC通信速度较慢,但可以在长距离内通信,且可以支持多主设备和多从设备的通信方式。
  3. UART(Universal Asynchronous Receiver/Transmitter)是一种异步的串行通信协议,需要2根线:TX和RX。UART通信速度较慢,但可以在长距离内通信,且可以支持点对点的通信方式。
  4. CAN:为了保证传输的安全性,相较于前面三种方法,使用了大量的手段保证数据及时、准确的传输。同时使用识别码,解决总线冲突的问题。这保证的优先级高的设备,先通讯。这也是为什么CAN能够用于工业、汽车等需要安全性高的领域。

二、SPI协议(全双工、速度快)

SPI 是英语Serial Peripheral interface的缩写,顾名思义就是串行外围设备接口。是Motorola(摩托罗拉)首先在其MC68HCXX系列处理器上定义的。SPI是一种高速的,全双工,同步的通信总线,可以在同一时间发送和接收数据,并且没有定义速度限制,通常能达到甚至超过10M/bps。在芯片的管脚上只占用四根线,节约了芯片的管脚,同时为PCB的布局上节省空间,提供方便,主要应用在 EEPROM,FLASH,实时时钟,AD转换器,还有数字信号处理器和数字信号解码器之间。

1.SPI特性

SPI总线包括4条逻辑线,定义如下:

MISOMaster input slave output 主机输入,从机输出(数据来自从机);

MOSIMaster output slave input 主机输出,从机输入(数据来自主机);

SCLKSerial Clock 串行时钟信号,由主机产生发送给从机;

SSSlave Select 片选信号,由主机发送,以控制与哪个从机通信,通常是低电平有效信号。

其他制造商可能会遵循其他命名规则,但是最终他们指的相同的含义。以下是一些常用术语;

MISO也可以是SDI,DIN或DI;

MOSI也可以是SDO,DOUT或DO;

NSS也可以是CE,CS或SSEL;

SCLK也可以是SCK;

本文将按照以下命名进行讲解[MISO, MOSI, SCK,CS]
 

2.SPI工作流程

SPI是一个同步的数据总线,它是用一条单独的数据线和一条单独的时钟信号来保证发送端和接收端的完美同步。

时钟是一个振荡信号,它告诉接收端在确切的时机对数据线上的信号进行采样。

SPI有主、从两种模式,通常由一个主模块(主机)和一个或多个从模块(丛机)组成(SPI不支持多主机),主模块选择一个从模块进行同步通信,从而完成数据的交换。提供时钟的为主设备(Master),接收时钟的设备为从设备(Slave),SPI接口的读写操作,都是由主设备发起,当存在多个从设备时,通过各自的片选信号进行管理。

数据的采集时机可能是时钟信号的上升沿(从低到高)或下降沿(从高到低)。

整体的传输大概可以分为以下几个过程:

1、主设备发起信号,将CS/SS拉低,启动通信。

2、主设备通过发送时钟信号,来告诉从设备进行写数据或者读数据操作(采集时机可能是时钟信号的上升沿(从低到高)或下降沿(从高到低),因为SPI有四种模式,后面会讲到),它将立即读取数据线上的信号,这样就得到了一位数据(1bit)。

3、主机(Master)将要发送的数据写到发送数据缓存区(Menory),缓存区经过移位寄存器(缓存长度不一定,看单片机配置),串行移位寄存器通过MOSI信号线将字节一位一位的移出去传送给从机,同时MISO接口接收到的数据经过移位寄存器一位一位的移到接收缓存区。

4、从机(Slave)也将自己的串行移位寄存器(缓存长度不一定,看单片机配置)中的内容通过MISO信号线返回给主机。同时通过MOSI信号线接收主机发送的数据,这样,两个移位寄存器中的内容就被交换。

        例如,下图示例中简单模拟SPI通信流程,主机拉低CS片选信号,启动通信,并且产生时钟信号,上升沿触发边沿信号,主机在MOSI线路一位一位发送数据0X53,在MISO线路一位一位接收数据0X46,如下图所示:

        SPI是“全双工”(具有单独的发送和接收线路), SPI只有主模式和从模式之分,没有读和写的说法,可以在同一时间发送和接收数据。另外SPI的接收硬件可以是一个简单的移位寄存器。这比异步串行通信所需的完整UART要简单得多,并且更加便宜;

        如果只进行写操作,主机只需忽略接收到的字节;反之,若主机要读取从机的一个字节,就必须发送一个空字节来引发从机的传输。也就是说,你发一个数据必然会收到一个数据;你要收一个数据必须也要先发一个数据。

        数据在传输中,高位在先还是低位在先,SPI协议并无明确规定,但是数据要在主从机中正确传输,自然双方要先约定好,一般会采用高位在先(MSB)方式传输。

3.SPI四种工作模式

除了配置串行时钟速率(频率)外,SPI主设备还需要配置时钟极性和时钟相位。

时钟极性 CKP/Clock Polarit

根据硬件制造商的命名规则不同,时钟极性通常写为CKP或CPOL。时钟极性和相位共同决定读取数据的方式,比如信号上升沿读取数据还是信号下降沿读取数据;

CKP可以配置为1或0。这意味着您可以根据需要将时钟的默认状态(IDLE)设置为高或低。极性反转可以通过简单的逻辑逆变器实现。您必须参考设备的数据手册才能正确设置CKP和CKE。CKP = 0:时钟空闲IDLE为低电平 0;
CKP = 1:时钟空闲IDLE为高电平1;


时钟相位 CKE /Clock Phase (Edge)
除配置串行时钟速率和极性外,SPI主设备还应配置时钟相位(或边沿)。根据硬件制造商的不同,时钟相位通常写为CKE或CPHA

顾名思义,时钟相位/边沿,也就是采集数据时是在时钟信号的具体相位或者边沿;

CKE = 0:在时钟信号SCK的第一个跳变沿采样;
CKE = 1:在时钟信号SCK的第二个跳变沿采样;

此处附上一组软件模拟SPI 通信传输数据的代码,方便各位理解其实现的方法。

//SPI发送数据,val为要发送的数据
void SPI_Send(u8 val)  
{ 

	u8 recv_data = 0, i = 0;//将接受数据清零方便直接或运算接受数据
	
	SCK = 0;//时钟线拉低

	for(i=0; i<8; i++) //传输数据,先发高位
	{
		//准备数据
		if(val  & (1<<(7-i))) //通过1左移7位然后相与,判断数据最高位是1还是0
		{
			MOSI = 1; //数据为1
		}
		else
		{
			MOSI = 0; //数据为0
		}
		delay_us(5);
		
		SCK = 1; //时钟线拉高准备接收数据
		delay_us(5);

		//高电平区间接收数据,此处不重要但必须有
		if(MISO == 1)	//收到的为1
		{
			recv_data |= (1<<(7-i));//将数据位通过或运算保存。先保持高位
		}
		SCK = 0;//时钟线拉低
	}
}

//SPI接收数据
u8 SPI_Receive(void)  
{ 
	u8 recv_data = 0, i = 0;
	
//	u8 val = 0x00;
	
	RC522_SCK = 0;
	for(i=0; i<8; i++) //发送数据,此处不重要但必须有
	{
		//准备数据
//		if(val  & (1<<(7-i))) //1
//		{
//			MOSI = 1; //数据为1
//		}
//		else
//		{
//			MOSI = 0; //数据为0
//		}
		
        MOSI = 0; //输出线清零
		delay_us(5);
		
		SCK = 1; //时钟线拉高准备接收数据
		delay_us(5);

		//高电平区间接收数据
		if(MISO == 1)	//收到的为1
		{
			recv_data |= (1<<(7-i));//将数据位通过或运算保存。先保持高位
		}

		SCK = 0;
	}
	return recv_data; //返回接收到的数据
}

数据传输流程

首先主机和从机都选择同一传输模式。然后主机片选拉低,选中从机。接着在时钟的驱动下, MOSI发送数据,同时MISO读取接收数据。最后完成传输,取消片选。

 /*
* 函数名: void SPI_WriteByte(uint8_t data)
* 输入参数: data -> 要写的数据
* 输出参数:无  
* 返回值:无
* 函数作用:模拟 SPI 写一个字节
*/ SPI写1 Byte,循环8次,每次发送1 Bit;
void SPI_WriteByte(uint8_t data)  {
    uint8_t i = 0;  
    uint8_t temp = 0;  
    for(i=0; i<8; i++) {
        temp = ((data&0x80)==0x80)? 1:0;  //将data最高位保存到temp;
        data = data<<1;                   //data左移一位,将次高位变为最高位,用于下次取最高位;
        SPI_CLK(0); //CPOL=0              //拉低时钟,即空闲时钟为低电平, CPOL=0;
        SPI_MOSI(temp);                   //根据temp值,设置MOSI引脚的电平;
        SPI_Delay();                      //简单延时,可以定时器或延时函数实现
        SPI_CLK(1); //CPHA=0  //拉高时钟, W25Q64只支持SPI模式0或1,即会在时钟上升沿采样MOSI数据;
        SPI_Delay();  
     }
     SPI_CLK(0);                          //最后SPI发送完后,拉低时钟,进入空闲状态;
}

/*
* 函数名: uint8_t SPI_ReadByte(void)
* 输入参数:
* 输出参数:无
* 返回值:读到的数据
* 函数作用:模拟 SPI 读一个字节
*/  SPI读1 Byte,循环8次,每次接收1 Bit;  
uint8_t SPI_ReadByte(void) {
    uint8_t i = 0;
    uint8_t read_data = 0xFF;
    for(i=0; i<8; i++) {
        read_data = read_data << 1;  //“腾空” read_data最低位,8次循环后,read_data将高位在前;  
        SPI_CLK(0);                  //拉低时钟,即空闲时钟为低电平;  
        SPI_Delay();
        SPI_CLK(1);
        SPI_Delay();
        if(SPI_MISO()==1) { 
           read_data = read_data + 1;
        }
    }
    SPI_CLK(0);   //最后SPI读取完后,拉低时钟,进入空闲状态  
    return read_data;
}  

 前面提到SPI传输可以看作一个虚拟的环形拓扑结构,即输入和输出同时进行。在前面“ SPI_WriteByte()”函数里,发送了1 Byte,也应该接收1 Byte,只是代码中忽略了接收引脚MISO的状态; 在前面“ SPI_ReadByte()”函数里,接收了1 Byte,也应该发送1 Byte,只是代码中忽略了发送引脚MOSI的内容。有些场景, SPI需要同时读写,因此还需要编写SPI同时读写函数。
 

/*
* 函数名: uint8_t SPI_WriteReadByte(uint8_t data)
* 输入参数: data -> 要写的一个字节数据
* 输出参数:无
* 返回值:读到的数据
* 函数作用:模拟 SPI 读写一个字节
*/SPI读和写1 Byte,循环8次,每次发送和接收1 Bit;  
uint8_t SPI_WriteReadByte(uint8_t data) {
    uint8_t i = 0;
    uint8_t temp = 0;
    uint8_t read_data = 0xFF;
    for(i=0;i<8;i++) {
        temp = ((data&0x80)==0x80)? 1:0; //将data最高位保存到temp;  
        data = data<<1;                  //data左移一位,将次高位变为最高位,用于下次取最高位;  
        read_data = read_data<<1;        //“腾空” read_data最低位,8次循环后,read_data将高位在前;  
        SPI_CLK(0);
        SPI_MOSI(temp);
        SPI_Delay();
        SPI_CLK(1);
        SPI_Delay();
        if(SPI_MISO()==1) {             //读取MISO上的数据,保存到当前read_data最低位;  
            read_data = read_data + 1;
        }
    }
    SPI_CLK(0);
    return read_data;
}  

三、IIC协议(半双工、速度中等)

IIC(Inter-Integrated Circuit)总线是一种由NXP(原PHILIPS)公司开发的两线式串行总线,用于连接微控制器及其外围设备。多用于主控制器和从器件间的主从通信,在小数据量场合使用,传输距离短,任意时刻只能有一个主机等特性。

在 CPU 与被控 IC 之间、IC 与 IC 之间进行双向传送,高速 IIC 总线一般可达 400kbps 以上。IIC是为了与低速设备通信而发明的,所以IIC的传输速率比不上SPI

1.IIC数据接口

IIC只有两个总线: 一条是双向的串行数据线SDA,一条是串行时钟线SCL

SDA(Serial data)是数据线,D代表Data也就是数据,Send Data也就是用来传输数据。

SCL(Serial clock line)是时钟线,C代表Clock也就是时钟,用来控制数据发送的时序。

所有接到I2C总线设备上的串行数据SDA都接到总线的SDA上,各设备的时钟线SCL接到总线的SCL上。I2C总线上的每个设备都自己一个唯一的地址,来确保不同设备之间访问的准确性。

2.IIC主要特点

通常我们为了方便把IIC设备分为主设备和从设备,基本上谁控制时钟线(即控制SCL的电平高低变换)谁就是主设备。

IIC主设备功能:主要产生时钟,产生起始信号和停止信号

IIC从设备功能:可编程的IIC地址检测,停止位检测

IIC的一个优点是它支持多主控(multimastering), 其中任何一个能够进行发送和接收的设备都可以成为主总线。一个主控能够控制信号的传输和时钟频率。当然,在任何时间点上只能有一个主控。支持不同速率的通讯速度,标准速度(最高速度100kHZ),快速(最高400kHZ)。

SCL和SDA都需要接上拉电阻 (大小由速度和容性负载决定一般在3.3K-10K之间) 保证数据的稳定性,减少干扰。

IIC是半双工,而不是全双工 ,同一时间只可以单向通信

为了避免总线信号的混乱,要求各设备连接到总线的输出端时必须是漏极开路(OD)输出或集电极开路(OC)输出。

IIC的高阻态
漏极开路(Open Drain)即高阻状态,适用于输入/输出,其可独立输入/输出低电平和高阻状态,若需要产生高电平,则需使用外部上拉电阻

高阻状态:高阻状态是三态门电路的一种状态。逻辑门的输出除有高、低电平两种状态外,还有第三种状态——高阻状态的门电路。电路分析时高阻态可做开路理解。

我们知道IIC的所有设备是接在一根总线上的,那么我们进行通信的时候往往只是几个设备进行通信,那么这时候其余的空闲设备可能会受到总线干扰,或者干扰到总线,怎么办呢?

为了避免总线信号的混乱,IIC的空闲状态只能有外部上拉, 而此时空闲设备被拉到了高阻态,也就是相当于断路, 整个IIC总线只有开启了的设备才会正常进行通信,而不会干扰到其他设备。
 

3.工作流程介绍

(1)主机向从机写入数据过程

Start: IIC开始信号,表示开始传输。
DEVICE_ADDRESS:: 从设备地址,就是7位从机地址。
R/W: W(write)为写,R(read)为读。
ACK: 应答信号。
WORD_ADDRESS : 从机中对应的寄存器地址 比方说访问 OLED中的 某个寄存器。
DATA: 发送的数据。
STOP: 停止信号。结束IIC通信。

流程介绍:

       1、主机首先产生START信号;

        2、然后发送一个从机地址,这个地址共有7位,紧接着的第8位是数据方向位(R/W),“0”表示主机向从机发送数据(写),“1”表示主机从从机接收数据(读);

        3、主机发送地址时,总线上的每个从机都将这7位地址码与自己的地址进行比较,若相同,则认为自己正在被主机寻址,根据R/W位将自己确定为发送器和接收器;

        4、这时候主机等待从机的应答信号(ACK);

        5、当主机收到应答信号时,发送要访问从机的那个地址, 继续等待从机的应答信号;

        6、当主机收到应答信号时,发送N个字节的数据,继续等待从机的N次应答信号;

        7、主机产生停止信号,结束传输过程。还有一种情况是从机发送非应答信号表示接受完毕后主机产生停止信号结束传输过程。
 

(2)主机读取从机数据过程

        1、主机首先产生START信号;

        2、然后紧跟着发送一个从机设备地址,注意此时该地址的第8位为0,表明是向从机写命令;

        3、这时候主机等待从机的应答信号(ACK);

        4、当主机收到应答信号时,发送要访问的寄存器地址,继续等待从机的应答信号;

        5、当主机收到应答信号后,主机要改变通信模式(主机将由发送变为接收,从机将由接收变为发送)所以主机重新发送一个开始start信号,然后紧跟着发送一个从机地址,注意此时该地址的第8位为1,表明将主机设置成接收模式开始读取数据;

        6、这时候主机等待从机的应答信号,当主机收到应答信号时,就可以接收1个字节的数据,当接收完成后,主机发送非应答信号(NACK),表示不再接收数据;

        7、主机进而产生停止信号,结束传送过程。

4.IIC常见问题

(1)为什么Open-Drain开漏输出需要上拉电阻

        开漏Pin不连接外部的上拉电阻,则只能输出低电平。当输出电平为低时,N沟道MOS管是导通的,这样在Vcc和GND之间有一个持续的电流流过上拉电阻R和三极管Q1。这会影响整个系统的功耗。采用较大值的上拉电阻可以减小电流。但是大的阻值会使输出信号的上升时间变慢。即上拉电阻R pull-up的阻值 决定了逻辑电平转换的沿的速度。阻值越大,速度越低功耗越小。反之亦然。

(2)为什么IIC需要漏极开路

防止短路
        如果不设为开漏,而设为推挽,几个设备连在同一条总线上,这时某一设备的某个IO输出高电平,另有一台设备的某一个IO输出低电平,这时你会发现这两个IO的VCC和GND短路了;但是开漏就不会有这个问题。

增强端口扇出能力、降低功耗
  IC为增强端口扇出能力而设计为漏极开路样式,使用时将该端口设为低电平有效的灌电流模式,能得到最大输出电流同时IC功耗最低。此类端口当输出高电平则需要外接上拉电阻。

利用“线与”判断总线占用状态
  可以将多个开漏输出的Pin脚,连接到一条线上,形成“与逻辑”关系,即“线与”功能,任意一个变低后,开漏线上的逻辑就为0了。这也是I2C,SMBus等总线判断总线占用状态的原理。
如果总线上的一个A设备将SDA拉高,这时总线上另一个B设备已将SDA拉低,这时由于1&0=0,所以A设备检查SDA的时候会发现不是高电平而是低电平,这就表明总线上已经有其他设备占用总线了,A只好放弃,如果检测是高电平那就可以使用。

增加驱动能力
        如果在漏极drain_output接上拉电阻,则可以进行电平转换,且驱动能力较强。
利用外部电路的驱动能力,减少IC内部的驱动。当IC内部MOSFET导通时,驱动电流是从外部的VCC流经R pull-up ,MOSFET到GND。IC内部仅需很下的栅极驱动电流。

控制输出高电平大小
        可以利用改变上拉电源的电压,改变传输电平。

(3)软件IIC和硬件IIC的区别

软件IIC:软件IIC通信指的是用单片机的两个I/O端口模拟出来的IIC,用软件控制管脚状态以模拟I2C通信波形,软件模拟寄存器的工作方式。

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

硬件I2C的效率要远高于软件的,而软件I2C由于不受管脚限制,接口比较灵活。

STM32硬件I2C与软件模拟I2C超详解_stm32 i2c-优快云博客

四、URAT协议(全双工、速度慢)

1.UART协议概念

UART的全称是通用异步收发器(Universal Asynchronous Receiver/Transmitter)
Universal 通用性体现在UART使用范围广上,作为一个通用的接口协议,UART广泛的应用在各类MCU和SOC产品上。
Asynchronous 异步性体现在“不需要额外的时钟线进行数据的同步传输”即只要信号拉低,即可开始传送数据,而另一些通讯协议,需要引入时钟信号来进行操作,如SPI需要在时钟的边沿发送数据。
Receiver/Transmitter 收发器则更好理解,即一个数据的发送方和一个数据的接收方,也意味着在数字IC设计中需要分别设计Receiver和Transmitter。

2.UART的帧格式

UART的一帧由起始位、数据位、校验位和停止位组成。数据逐位传输,如下图所示

(1)为什么UART的传输需要起始位?

因为UART没有控制线,要让接收方知道什么时候开始接收数据,需要一些手段,UART数据的传输中,只有一根线,所以在发送数据之前,先发一位逻辑“0”作为数据发送的起始标志,接收方在空闲时,当检测到有一个低电平,则开始接逐位接收数据。

(2)UART基本的数据形式
  • 默认无传输数据时,为高电平
  • 当信号拉低,传输线上的电平拉低,意味着开始进行数据传输
  • 紧接着起始位的是数据位,它可以是5、6、7或8位
  • UART的“校验位”紧挨着“数据位”,采用奇偶校验方式,根据设置,校验位可以存在也可以不存在
  • UART将停止位作为停止标志,是在数据位(没有校验位)和校验位(有校验位)之后发送1~2位的逻辑“1”高电平。当发送完停止位之后,UART总线进入空闲。

(3)为什么UART的数据位可变?

因为UART是一种低速总线,每多发一位都占用不少的时间(由传输波特率决定),所以可以根据传输数据的特点,采用不同位宽以节约数据传输的时间

3.UART的波特率

如果从更高的level审视UART传输协议,如嵌入式开发者的角度,我们会发现,在使用具体的UART协议前,我们需要对发送端和接收端进行波特率的同步,以此来确保发射端的数据可以在接收端得到正确的采集。常用的波特率可以是300,1200,2400,9600,19200,38400,115200,这些数意味着什么呢?别着急,我们接下来要讨论这个内容。

(1)什么是波特率

波特率等于每秒钟传输的数据位数,假如我们的全局时钟频率为100MHz,波特率设置为9600,那么意味着每秒该UART传输协议可以传输9600bits的数据,换句话说传输1比特需时间约为:10^9(ns)/9600=104166(ns)。

(2)如何换算波特率

书接上文,时钟频率假如为100MHz,这意味着我们的时钟周期为10ns,因此10416个时钟周期我们就可以传输1bit数据,换言之我们需要一个大小为10416的分频电路来对100MHz时钟进行处理,因此在设计UART的过程中,我们需要使用分频电路依据波特率处理全局时钟,依据分频后的时钟节奏来发送数据和接收数据。

4.UART的缺点

(1)电气接口不统一

UART只是对信号的时序进行了定义,而未定义接口的电气特性;
UART通信时一般直接使用处理器使用的电平,即TTL电平,但不同的处理器使用的电平存在差异,所以不同的处理器使用UART通信时一般不能直接相连;
UART没有规定不同器件连接时连接器的标准,所以不同器件间通过UART通信时连接很不方便。

(2)抗干扰能力差

UART一般直接使用TTL信号来表示0和1,但TTL信号的抗干扰能力较差,数据在传输过程中很容易出错。

(3)通信距离极短

因为TTL信号的抗干扰能力较差,所以其通信距离也很短,一船只能用于一个电路板上的两个不芯片之间的通信。

5.RS232标准

缺点:

接口的信号电平值较高,易损坏接口电路的芯片,又因为与TTL电平不兼容,所以需要使用电平转换芯片才能与TTL电路连接
通信速度较低。
易产生共模干扰,抗噪声干扰性弱。
传输距离较短(15m)。
 

五、CAN通信(半双工)

1.CAN通信概念

CAN通信是一种多节点通信协议,最早由Bosch公司开发并在1986年首次推出。它被广泛应用于汽车电子控制系统、工业自动化领域以及其他需求多节点通信的应用场中。

CAN通信的特点之一是支持多节点之间的高速数据传输,适用于需要高实时性和高可靠性的系统。CAN总线由两根线组成,分别是CAN_H(CAN High)CAN_L(CAN Low)。CAN总线使用不同的电压电平来表示0和1,并通过差分信号传输来抗干扰。

2.CAN通信概念

CAN通信采用CSMA/CD(Carrier Sense Multiple Access with Collision Detection)的工作原理。这意味着每个节点都可以在总线上发送消息,但在发送之前需要先监听总线上的通信情况。

当一个节点要发送消息时,首先会监听总线,如果没有其他节点正在发送消息,它就可以开始发送。如果同时有多个节点尝试发送消息,就会发生冲突。在CAN总线上使用的是非毁坏性冲突检测机制,冲突的节点会立即停止发送,并在发送完自己的消息后再次来检测冲突。

CAN通信中还使用了位定时传输方式,即总线上的每个位都有固定的时间段。发送节点将每个位的电平保持一段时间,接收节点则在相应的时间段内检测位的电平。这种位定时传输方式确保了数据的同步和准确性。

此外,CAN通信还通过帧的优先级来管理消息的传输。较低优先级的帧会在总线上等待较高优先级的帧发送完毕后再发送,确保重要消息的及时传输。

3. CAN通信的应用领域

CAN通信被广泛应用于各种领域,特别是在汽车和工业控制系统中。

在汽车领域,CAN通信用于连接汽车的各个控制单元,如发动机控制单元(ECU)、刹车系统、仪表盘等。CAN总线提供了高速、实时的数据传输,使得这些控制单元能够相互通信和协调工作,实现车辆的高效控制和监测。

在工业控制系统中,CAN通信被用于连接各种设备和传感器,例如机器人、PLC(可编程逻辑控制器)、传感器网络等。通过CAN总线,这些设备可以实现实时数据交换和远程控制,从而提高生产效率和系统的可靠性。

除了汽车和工业控制,CAN通信还应用于其他领域,包括航空航天、医疗设备、能源管理等。CAN通信的高可靠性和抗干扰能力使其成为处理实时数据和多节点通信的理想选择。

4. CAN帧格式与标识符

CAN通信使用帧格式来传输数据。CAN帧分为标准帧扩展帧两种格式。

标准帧由11位标识符、数据域、控制域和CRC(循环冗余校验)组成。标识符用于标识消息的优先级和内容,数据域用于传输实际的数据,控制域包含帧的控制信息,而CRC用于发送节点计算校验和,接收节点用于验证数据的完整性。

扩展帧使用29位标识符,其他组成部分与标准帧相同。扩展帧的使用使得CAN网络能够处理更多的节点和更大的数据量。

标识符的选择对于CAN通信至关重要。帧使用的标识符决定了其在总线上的优先级,较低的标识符意味着较高的优先级。在设计和配置CAN网络时,需要合理设置标识符以确保系统的正确运行。

5. CAN通信模式

CAN通信有几种不同的模式,可以通过配置CAN控制器的寄存器来选择适合应用需求的模式。

常见的CAN通信模式包括:

  • 正常模式(Normal Mode):用于实际通信,节点能够发送和接收数据。
  • 监听模式(Listen-Only Mode):节点只能监听总线上的通信,但不能发送消息。
  • 回环模式(Loopback Mode):发送的帧会回环到本地接收,用于自测和调试。
  • 静默模式(Silent Mode):节点只能监听总线上的通信,不能发送消息,并向其他节点传递错误状态。

6. 使用HAL库实现CAN通信

对于使用HAL(Hardware Abstraction Layer)库进行STM32开发的用户,HAL库提供了高级的抽象接口和简洁的函数调用,方便编写和管理CAN通信。

6.1 STM32 CAN通信配置:

a. 使能CAN外设:在初始化阶段,需要使能所选择的CAN外设,并配置相应的时钟。

b. 配置CAN控制器:通过设置CAN控制器的特殊寄存器,配置CAN通信的参数,包括波特率、工作模式、过滤器等。

c. 配置GPIO引脚:将相关的GPIO引脚配置为CAN模式,以实现CAN数据的收发。

d. 初始化CAN通信:通过初始化CAN控制器的寄存器,准备CAN通信的环境,包括清除任何悬空状态和错误标志。

e. 开始CAN通信:使能CAN控制器的接收和发送功能,使其处于工作状态。

6.2 整体代码

// 包含所需的头文件
#include "stm32f4xx_hal.h"

// CAN消息结构体
CAN_TxHeaderTypeDef TxHeader;
CAN_RxHeaderTypeDef RxHeader;
uint8_t TxData[8];
uint8_t RxData[8];

void CAN_Configuration(void)
{
  // 初始化CAN控制器
  hcan.Instance = CAN1;
  hcan.Init.Prescaler = 10;
  hcan.Init.Mode = CAN_MODE_NORMAL;
  hcan.Init.SyncJumpWidth = CAN_SJW_1TQ;
  hcan.Init.TimeSeg1 = CAN_BS1_8TQ;
  hcan.Init.TimeSeg2 = CAN_BS2_7TQ;
  hcan.Init.TimeTriggeredMode = DISABLE;
  hcan.Init.AutoBusOff = ENABLE;
  hcan.Init.AutoWakeUp = DISABLE;
  hcan.Init.AutoRetransmission = ENABLE;
  hcan.Init.ReceiveFifoLocked = DISABLE;
  HAL_CAN_Init(&hcan);

  // 配置CAN过滤器
  CAN_FilterTypeDef sFilterConfig;
  sFilterConfig.FilterBank = 0;
  sFilterConfig.FilterMode = CAN_FILTERMODE_IDMASK;
  sFilterConfig.FilterScale = CAN_FILTERSCALE_32BIT;
  sFilterConfig.FilterIdHigh = 0x0000;
  sFilterConfig.FilterIdLow = 0x0000;
  sFilterConfig.FilterMaskIdHigh = 0x0000;
  sFilterConfig.FilterMaskIdLow = 0x0000;
  sFilterConfig.FilterFIFOAssignment = CAN_FILTER_FIFO0;
  sFilterConfig.FilterActivation = ENABLE;
  sFilterConfig.SlaveStartFilterBank = 14;
  HAL_CAN_ConfigFilter(&hcan, &sFilterConfig);

  // 启动CAN通信
  HAL_CAN_Start(&hcan);

  // 配置CAN消息头
  TxHeader.StdId = 0x123;
  TxHeader.ExtId = 0x00;
  TxHeader.RTR = CAN_RTR_DATA;
  TxHeader.IDE = CAN_ID_STD;
  TxHeader.DLC = 8;
  TxHeader.TransmitGlobalTime = DISABLE;
}

void CAN_SendData(uint8_t* data, uint32_t length)
{
  // 填充发送消息的数据
  for (uint8_t i = 0; i < length; i++) {
  TxData[i] = data[i];
  }

  // 发送消息
  if (HAL_CAN_AddTxMessage(&hcan, &TxHeader, TxData, &TxMailbox) == HAL_OK) {
  HAL_CAN_Transmit(&hcan, 100);
  }
}

void CAN_ReceiveData(void)
{
  // 接收消息
  if (HAL_CAN_GetRxFifoFillLevel(&hcan, CAN_RX_FIFO0) > 0) {
  if (HAL_CAN_GetRxMessage(&hcan, CAN_RX_FIFO0, &RxHeader, RxData) == HAL_OK) {
  // 处理接收到的数据
  // …
    }
  }
}

int main(void)
{
  // 初始化HAL库和其他外设

  // 配置CAN通信
  CAN_Configuration();

  while (1) {
  // 发送数据
  uint8_t sendData[] = {0x01, 0x02, 0x03};
  CAN_SendData(sendData, sizeof(sendData));

  // 接收数据
  CAN_ReceiveData();
  }
}

<think>好的,我现在需要帮用户解答关于工业机械臂、伺服电机、六轴机械臂在嵌入式MCU层面的面试考点和解答。首先,用户之前已经询问过PID控制器的输出结果,可能是在准备相关职位的面试,比如嵌入式系统工程师或者控制算法工程师。现在他转向更具体的应用场景,涉及机械臂和伺服电机的嵌入式开发。 首先,用户的问题涉及三个方面:工业机械臂、伺服电机、六轴机械臂,在嵌入式MCU层面的面试考点。我需要从这几个方面分别分析可能的考点,并提供解答思路。因为用户之前问的是PID,可能面试中会涉及到控制算法,比如PID在伺服电机中的应用,以及机械臂的运动控制。 接下来,我需要考虑嵌入式MCU的具体知识点。MCU层面可能涉及实时操作系统(RTOS)、中断处理、PWM生成、编码器接口、通信协议(如CANSPI、I2C)、电机驱动、运动学算法、多轴同步控制等。同时,机械臂的六轴结构可能涉及到逆运动学、轨迹规划、插补算法等。 然后,考虑用户可能的背景。他可能在准备面试,需要具体的考点和解答,所以需要结构清晰,分点列出,每个考点下给出解答示例。此外,用户可能希望了解常见面试问题以及如何回答,比如如何处理多轴同步、如何实现高精度控制、如何优化实时性能等。 还要注意用户提到的嵌入式层面,所以问题可能集中在硬件接口、驱动开发、实时性处理、低层算法实现等方面,而不是上层的应用开发。例如,如何用STM32的定时器生成PWM控制伺服电机,如何处理编码器反馈,如何设计通信协议与上位机交互等。 另外,六轴机械臂涉及较多的运动学计算,可能需要考察逆运动学的实现,如何在资源受限的MCU上高效计算,或者使用何种算法(如雅可比矩阵法、数值解法等)。同时,嵌入式系统中的内存管理、计算优化(如定点数运算代替浮点)也是考点。 伺服电机的控制可能涉及位置环、速度环、电流环的三环控制,以及如何调整PID参数。在MCU上实现这些控制环需要考虑中断频率、计算时间、实时性保证等问题。还可能问到故障保护机制,比如过流检测、堵转处理等。 其他可能的考点包括多任务调度(使用RTOS或裸机)、外设配置(如ADC读取力传感器数据)、通信总线(如CAN总线在工业机械臂中的应用)、EMC设计、低功耗考虑等。 在解答时,需要结合实际例子,比如使用STM32H7系列的高性能MCU,结合FreeRTOS进行任务调度,通过定时器中断实现精确的PWM输出,利用DMA传输数据减少CPU负担,或者使用硬件加速模块进行矩阵运算以提高逆运动学计算速度。 同时,应该注意面试中常问的项目经验问题,比如描述一个你参与过的机械臂控制项目,遇到的技术挑战及如何解决。这可能要求用户结合具体案例,展示问题分析和解决能力。 最后,需要提醒用户注意代码优化、实时性测试、调试方法(如使用逻辑分析仪或示波器排查时序问题),以及相关的行业标准或安全规范。这些都是嵌入式系统开发中不可忽视的部分。</think>以下是针对工业机械臂、伺服电机和六轴机械臂在嵌入式MCU层面的面试考点及解答框架,按技术层级分类解析: --- ### **一、基础硬件层考点** #### 1. 伺服电机驱动原理 **问题**:如何通过MCU实现伺服电WM信号控制? **解答**: - **PWM生成**:使用定时器配置为PWM模式(如STM32的TIMx_CHy),频率通常为1-20kHz,占空比对应电机目标位置。 - **死区控制**:互补PWM输出需配置死区时间(如高级定时器的BDTR寄存器),防止H桥上下管直通。 - **示例代码**: ```c HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_1); // 启动PWM通道 __HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_1, pulse_width); // 设置占空比 ``` #### 2. 编码器接口设计 **问题**:如何读取伺服电机的增量式编码器信号? **解答**: - **接口选择**:使用MCU的编码器模式(如STM32的TIMx编码器接口),直接捕获A/B相正交信号。 - **计数值处理**:配置定时器为上下计数模式,通过`TIMx->CNT`读取位置差值。 - **过速保护**:溢出中断处理(如ARR寄存器配置最大计数值)。 --- ### **二、控制算法层考点** #### 1. 多轴同步控制 **问题**:如何保证六轴机械臂的6个关节同步运动? **解答**: - **时间戳同步**:使用硬件定时器触发所有轴的PWM更新(如STM32的TRGO事件)。 - **插补算法**:在MCU中实现直线/圆弧插补,计算各轴步进量(需定点数优化)。 - **示例公式**(直线插补): $$x(t) = x_0 + vt\cdot \cos\theta,\quad y(t) = y_0 + vt\cdot \sin\theta$$ 需离散化为步进脉冲序列。 #### 2. 逆运动学实时计算 **问题**:如何在资源有限的MCU上实现逆运动学解算? **解答**: - **简化模型**:采用几何法替代雅可比矩阵(如PUMA型机械臂的封闭解)。 - **查表法**:预计算常用位姿的关节角,存储为查找表。 - **定点数优化**:使用Q格式(如Q15)代替浮点运算,减少计算耗时。 --- ### **三、实时系统层考点** #### 1. 任务调度设计 **问题**:如何设计嵌入式系统的实时任务优先级? **解答**: - **关键任务分级**: - 最高级:电机控制中断(<100μs响应) - 中级:轨迹规划(1ms周期) - 低级:通信处理(10ms周期) - **实现方案**:使用RTOS(如FreeRTOS)或裸机状态机。 #### 2. 中断冲突处理 **问题**:当PWM中断与编码器中断同时发生时,如何避免数据错误? **解答**: - **优先级配置**:编码器中断设为最高优先级(如NVIC_PriorityGroup_4)。 - **双缓冲机制**:在中断中更新"影子寄存器",主循环中同步实际控制量。 --- ### **四、调试与优化考点** #### 1. 控制环路延迟分析 **问题**:如何测量PID控制环的执行时间? **解答**: - **GPIO翻转法**:在控制环开始/结束时翻转引脚,用示波器测量脉冲宽度。 - **定时器计数**:在中断入口/出口读取`DWT->CYCCNT`(CPU周期计数器)。 #### 2. 抗干扰设计 **问题**:工业现场电磁干扰导致电机抖动,如何解决? **解答**: - **硬件层面**:增加磁环、优化PCB布局(模拟/数字地分割)。 - **软件层面**:对编码器信号进行数字滤波(如滑动平均滤波)。 --- ### **五、典型面试问题示例** **Q**: 机械臂运动时出现末端抖动,可能是什么原因? **A**: 1. **控制环路问题**:PID参数不当(如微分增益不足) 2. **机械共振**:需加入低通滤波器或Notch滤波器 3. **通信延迟**:CAN总线负载过高导致控制指令不同步 4. **电源干扰**:电机启停引起电压波动,需增加去耦电容 **Q**: 如何验证六轴机械臂的定位精度? **A**: 1. **激光跟踪仪**测量末端实际坐标 2. **重复性测试**:多次执行同一路径,统计偏差 3. **反向间隙补偿**:在固件中预存齿轮间隙补偿值 --- ### **六、加分项准备** 1. **行业协议**:熟悉EtherCAT/CANopen协议栈移植经验 2. **安全规范**:了解IEC 61508功能安全标准 3. **开源项目**:参考RT-Thread或ROS2-MicroXRCE的机械臂案例 通过上述分层解析,可系统覆盖从硬件驱动到运动控制的嵌入式MCU核心技术点,建议结合具体项目经验展开回答。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值