工程文件参考——STM32+HAL+SPI+CRC主从机通讯


前言

关于如何简单的写一个稳定的SPI主从机通讯,思路很简单
1、SPI高速传输的时候很容易出现错位之类的问题,CRC的校验首先是必要的。在STM32中SPI使用DMA通讯可以自动执行CRC的校验,只需要在CubeMX设置SPI的CRC和DMA
2、从机的NSS用硬件模式不是很好用,使用外部中断可以提供更多灵活的编写方式
3、有个很坑的点,如果中间出现断连等问题,很容易出现数据错位,而且错了一次会一直错下去,所以检测到CRC错误后要自己将SPI模块的时钟重置

20250107更新,我终于改出一版完全可用的多主从机方案了,总结以下
关于从机
1、Normal的DMA方案可以让整个程序变得更加简单,且circular模式不支持CRC循环
2、CRC是必须的,SPI通讯中出现传输错误是非常致命的
3、硬件NSS用起来不错,很值得使用
4、核心的回调使用HAL_SPI_TxRxCpltCallback和HAL_SPI_ErrorCallback,可以处理掉所有问题


CubeMX设置

SPI设置

推荐勾选CRC和硬件的NSS,CRC校验方式要和主机相同
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
这里Mode非常不推荐使用Circular,官方文件里明确写了Circular模式不支持CRC,不带CRC校验很难避免传输错误
我选的16bit传输,具体可以根据自己的硬件需求再改

SPI从机代码

放在哪里都行

//数据接收处理
void HAL_SPI_TxRxCpltCallback(SPI_HandleTypeDef *hspi)
{
	if (hspi == &hspi1)
	{
		if(HAL_SPI_GetError(&hspi1) == HAL_SPI_ERROR_NONE)//我个人觉得这个也不是很需要
		{
			memcpy(tgt_buf, spi1_rx_buf, 8);
			HAL_SPI_TransmitReceive_DMA(&hspi1, (uint8_t*)spi1_tx_buf, (uint8_t*)spi1_rx_buf, 4);//开了normal所以要重新开启
		}
		
	}
}
//通讯错误处理
void HAL_SPI_ErrorCallback(SPI_HandleTypeDef *hspi)
{
	if (hspi == &hspi1)
	{
		HAL_SPI_DeInit(&hspi1);
		HAL_SPI_Init(&hspi1);
		HAL_SPI_TransmitReceive_DMA(&hspi1, (uint8_t*)spi1_tx_buf, (uint8_t*)spi1_rx_buf, 4);
	}
}

//系统初始化之后记得增加一句
HAL_SPI_TransmitReceive_DMA(&hspi1, (uint8_t*)spi1_tx_buf, (uint8_t*)spi1_rx_buf, 4);

SPI主机代码

主机直接定时跑就够了

HAL_GPIO_WritePin(GPIOX, GPIO_PIN_X, GPIO_PIN_RESET);
HAL_SPI_TransmitReceive_DMA(&hspi1, (uint8_t*)spi1_tx_buf, (uint8_t*)spi1_rx_buf, 4);
HAL_GPIO_WritePin(GPIOX, GPIO_PIN_X, GPIO_PIN_SET);

CRC校验会自动在DMA传输完后自动再传输

总结讨论

网上讨论关于SPI从机的方案有很多,实测很多使用方法都有些问题

1、关于CRC是否需要让size+1,原文这里写的是pRxData要size+1,我个人理解是说Rx的缓冲区要比预期的大1,而不是说最后的size要写5。实际测试的时候,如果主机发4,从机接5,最后一位接到的是CRC的校验值,并且SPI模块自动的CRC会出错,所以我个人倾向是数据传输多少size就写多少
多次测试主机也是,比如你计划发送4,DMA传输中CRC会自动在第四位之后发送
在这里插入图片描述
此外关于pRxData是否需要+1我觉得比较可疑,实机运行过程中并不会观察到pRxData第五位内容的变化,保险起见可以在创建缓冲区的时候设计大一点

2、关于HAL_SPI_TxRxCpltCallback和HAL_SPI_ErrorCallback的回调触发,根据stm32g0xx_hal_spi.c文件中,任何通讯中的错误都会触发HAL_SPI_ErrorCallback,而只要触发HAL_SPI_ErrorCallback就不会触发HAL_SPI_TxRxCpltCallback,所以我个人觉得在HAL_SPI_TxRxCpltCallback中拿到的pRxData应该就是完全正确的,其实也并不需要额外检测下是否有通讯错误(包括CRC也会在其中校验出)。
另外关于关于通讯出错也是件相当胃疼的事情,在之前的测试中,通讯时序错位等等等都会导致通讯出问题,而且就像很多博主所说的连着后边的通讯统一错位(类似于3412的循环错误)。目前测试在使用硬件NSS方案中,在回调HAL_SPI_ErrorCallback的时候对spi模块进行重新初始化
我暂时没遇到时序错位的情况,如果有,可以重置下时钟

	__HAL_RCC_SPI1_FORCE_RESET();
	__HAL_RCC_SPI1_RELEASE_RESET();

3、实测过程中,在不使用硬件NSS的时候,多从机相互之间干扰也很严重,所以我推测使用软件NSS会导致非片选阶段NSS也会触发SPI的DMA通讯继续,后来使用硬件NSS就没这个问题了。

在这里插入图片描述
4、如果使用CRC校验的话,注意DMA别选circular,这俩是冲突的,没了CRC通讯非常容易跑飞,更建议在每次通讯完成后手动再设计新的传输
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值