嵌入式通信协议——“SPI通信”

        引言

        SPI协议被广泛用于嵌入式通信,由Motorola(现在的NXP)在1980年代提出,它是一种“同步、全双工”的通信协议。

        一、SPI的基本组成

        SPI由4根线完成通信,它们分别是SCL(时钟线 )、MOSI(主发从收)、MISO(主收从发)、CS(片选信号) 组成。因为有两根线完成数据的收发,因此通信速度是比IIC要快很多的。另外SPI的实现起来要比IIC简单的多,除了占用引脚和错误校验有不足之外,SPI还是很优秀的。

        二、SPI的四种通信模式

        SPI有四种通信模式,根据时钟的极性(CPOL )即空闲时时钟的电平,和数相位(CPHA )即采样时刻,可分为以下四种:

  • 模式0(CPOL = 0,CPHA = 0):时钟信号空闲时为低电平,数据在时钟信号的上升沿采样。
  • 模式1(CPOL = 0,CPHA = 1):时钟信号空闲时为低电平,数据在时钟信号的下降沿采样。
  • 模式2(CPOL = 1,CPHA = 0):时钟信号空闲时为高电平,数据在时钟信号的上升沿采样。
  • 模式3(CPOL = 1,CPHA = 1):时钟信号空闲时为高电平,数据在时钟信号的下降沿采样。

        三、SPI的接线方式

        SPI 接线并不像UART那样同名反接,而是同名同接,即从机的MOSI接主机的MOSI,从机的MISO接主机的MISO,在一些设备上,从机的接口可能是SO和SI,这连个也分别对应MISO和MOSI。时钟线和片选线都一样。

        四、SPI的模拟实现

        理论再扎实也比不过动手做一次,这里来实现一次SPI协议,在上次我写了一份W25Q64的驱动代码,用的硬件SPI,那这里我来用软件实现一次。

        先给四根通信线封装一层函数:

void MySPI_W_CSS(uint8_t bitval)
{
	HAL_GPIO_WritePin(SPI_FLASH_CCS_PORT,FLASH_CCS_PIN,(GPIO_PinState)bitval);
}

void MySPI_W_SCK(uint8_t bitval)
{
	HAL_GPIO_WritePin(SPI_FLASH_SCLK_PORT,FLASH_SCLK_PIN,(GPIO_PinState)bitval);
}

void MySPI_W_MOSI(uint8_t bitval)
{
	HAL_GPIO_WritePin(SPI_FLASH_MOSI_PORT,FLASH_MOSI_PIN,(GPIO_PinState)bitval);
}

GPIO_PinState MySPI_R_MISO()
{
	return HAL_GPIO_ReadPin(SPI_FLASH_MISO_PORT,FLASH_MISO_PIN);
}

        再封装数据交换的函数(模式0):

void MySPI_CS_High()
{
	MySPI_W_CSS(0);
}


void MySPI_CS_Low()
{
	MySPI_W_CSS(1);
}

//发送或者读取数据
uint8_t MySPI_SwapByte(uint8_t Byte)
{
	uint8_t i=0;
	for(i = 0;i < 8; i++)
	{
		MySPI_W_MOSI(Byte & 0x80);
		MySPI_W_SCK(1);
		Byte <<= 1;
		if(MySPI_R_MISO() == GPIO_PIN_SET)
		{
			Byte |= 0x01;
		}
		MySPI_W_SCK(0);
	}
	return Byte;
}

        因为这是全双工的通信协议,因此不能像IIC一样发送数据和交换数据封装为两个函数,另外STM32的HAL库有将SPI的发送和接收封装为两个函数的原因是接收数据和发送数据共用一个DR寄存器,数据可以储存在寄存器中来读写。软件SPI没有这种条件,因此需要在实现的过程中根据时序严格来读取和发送数据。

        因为我们先发送的高位,因此Byte在左移的过程中,高位被发送,同时接收到的数据的高位被写入到Byte的低位,循环往复8次,Byte的第0位被移动到第7位,最终可以接受到完整的一个字节以及发送完一个字节。

        将软件SPI替换到硬件SPI的代码,再进行测试:

void W25QxxTest()
{
	uint16_t head,tail;

	for(uint16_t i = 0;i < 8192;i++)
	{
		data[i] = '1';
	}

	printf("现在进行软件SPI实验!\n\n");
	
	uint32_t id = W25Qxx_GetID();
	printf("芯片ID为:%X\n\n",id);
	if(id == W25Q64_ID)
	{
		printf("正在擦除...\n");
		W25Qxx_BlockErase(0x000000);
		printf("擦除成功!\n\n");
		
		printf("正在写入...写入数据量为8kb\n");
		head = HAL_GetTick();
		W25Qxx_WriteData(0x00000A,data,8192);
		tail = HAL_GetTick();
		
		printf("写入成功!,写入花费的时间为:%d ms\n\n",tail - head);
	
		printf("正在读取数据,读取数据量为8kb\n");
		head = HAL_GetTick();
		W25Qxx_ReadData(0x00000A,test,8192);
		tail = HAL_GetTick();
		
		
		if(memcmp(test,data,8192) == 0)
		{
			printf("读取成功,读取花费的时间为:%d ms\n\n",tail - head);
		}
		else
		{
			printf("读取失败\n");

		}
			printf("读取到的数据是: %s\n",test);
		
	}



}

        五、软件SPI和硬件SPI的优缺点

        软件SPI和硬件SPI各有优劣。

软件SPI

        优点:

                1.易于移植

                2.灵活性高

                3.节省硬件资源

         缺点:

                1.速度慢

                2.CPU负担重,因为是CPU控制时序

硬件SPI

        优点:

        1.速度快,时序精准

        2.节省CPU资源

        3.稳定性好

        缺点:

        1.灵活性差,即固定的引脚通信

        2.消耗硬件资源

往期博客写文章-优快云创作中心

                                                                                                                                 

### 关于嵌入式系统的通信协议面试题 #### 1. UART (Universal Asynchronous Receiver/Transmitter) UART是一种常见的异步串行接口,在实际应用中,尽管其传输不依赖外部时钟信号,但仍需设置波特率以确保发送端与接收端的数据同步[^1]。 对于可能出现的通讯错误处理方法如下: - **校验机制**:采用奇偶校验、循环冗余检验(CRC)等方式提高数据可靠性。 - **超时重传策略**:当检测到帧间间隔超过预设时间,则认为前一帧已丢失并触发重新发送操作。 如果发生通讯丢失的情况,通常会采取以下措施恢复连接: - 检查物理层连线是否松动或损坏; - 调整双方设备的工作参数至匹配状态; - 使用更稳定的电源供应减少干扰源影响; ```c // 示例代码展示如何配置UART初始化函数中的波特率 void uart_init(unsigned int baudrate){ // 计算分频系数... } ``` #### 2. IIC (Inter-Integrated Circuit) IIC总线支持两种基本类型的事务——读取和写入。由于它仅使用两根线路(SDA/SCL),因此特别适合用于板级内部芯片间的短距离互联。 针对IIC通信过程中可能遇到的问题及其解决方案包括但不限于: - 主从节点地址冲突解决办法; - 数据碰撞预防技术; - 多主机仲裁算法实现原理; #### 3. SPI (Serial Peripheral Interface) SPI具有四种不同的工作模式,具体取决于CPOL(时钟极性)以及CPHA(时钟相位)两个属性的不同组合情况。 在设计基于SPI的应用程序时应注意区分目标外设所遵循的具体模式,并据此调整MCU相应寄存器配置。 ```cpp enum SPIMode { MODE_0, // CPOL=0, CPHA=0 MODE_1, // CPOL=0, CPHA=1 MODE_2, // CPOL=1, CPHA=0 MODE_3 // CPOL=1, CPHA=1 }; ``` #### 4. RS232 vs RS485 RS232标准定义了一种单端电气特性,适用于点对点之间较近距离内的全双工通信;而RS485则采用了差分信令方式,允许构建多站网络拓扑结构,具备更强抗噪能力和更远的有效范围。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值