Toggle navigation
首页
问答
文章
积分商城
专家
专区
更多专区...
文档中心
返回主站
搜索
提问
会员
中心
登录
注册
SPI从机
SPI 主从双机通讯的实现
1.00
发布于 2022-01-22 13:52:46 浏览:5808
订阅该版
[tocm] ## 前言 在嵌入式学习的路上,SPI 是一个非常常见的外设,但是大部分使用的场景是作为主机,关于从机的实现可以参考的例程比较少见,本着学习理解的态度,对 SPI 的双机通讯做了一些试验,发现一个好的 SPI 从机实现需要考虑的方方面面非常多,本文仅实现通选,未实现协议层的传输。 本文使用 [ART-Pi](https://art-pi.gitee.io/website/) ,单板完成 SPI 双机通讯试验,主机是 SPI2 ,从机使用 SPI4.   ### 一 ,SPI 的模式 SPI是串行外设接口(Serial Peripheral Interface)的缩写。是 Motorola 公司推出的一种同步串行接口技术,是一种高速的,全双工,同步的通信总线。 SPI 的主从双方必须设置一致的通讯模式才能完成正常的通讯,有部分厂商的模块可能会同时支持两种模式,但是在 MCU 之间的通讯只能配置为双方一致的模式。 SPI 通过 CPOL(时钟极性) 和 CPHA(时钟相位) 来确定数据的传输模式,因此有以下四种模式; | 模式 | 时钟极性 | 时钟相位 | | ----- | -------- | -------- | | 模式0 | CPOL = 0 | CPHA = 0 | | 模式1 | CPOL = 0 | CPHA = 1 | | 模式2 | CPOL = 1 | CPHA = 0 | | 模式3 | CPOL = 1 | CPHA = 1 | CPOL(时钟极性):表示 CLK 处于空闲时的状态,可以是高电平也可以是低电平 CPHA(时钟相位):表示数据采样的边沿以及发送的边沿。  从设备的时钟由主设备通过 SCLK 提供,MOSI、MISO 则基于此脉冲完成数据传输。 接线方式如下图所示:  ### 二 ,SPI 的工作模式 SPI 有 3 种工作模式,分别是轮询,中断和 DMA,因为 SPI 是全双工,发送数据的同时也会收到数据,也就是在 SPI 的 CLK 产生之后,发送和接收数据寄存器里面保存的就是实际的数据。本次实验主机使用轮询模式,从机使用什么模式接下来对不同的模式进行分析。下图描述了 SPI 传输的过程  1. 轮询模式 轮询模式顾名思义不断的轮训检查 ```c while ((initial_TxXferCount > 0UL) || (initial_RxXferCount > 0UL)) { ... } ``` 检查要接收的数据和要发送的数据是否为 0 ,如果不为 0 ,就是不断检查收发寄存器的状态标志位,直到接收到预期的数据或者直至超时退出 。 一般场景主机使用轮询模式即可,因为从机需要接收到主机的 CLK 之后才能收发数据,从机使用轮询模式,那么就需要从机一直守在这,一直检查寄存器,那从机就干不了其他事情了,因此从机不适合使用轮询模式。 2. 中断模式 中断模式下会根据对 SPI 的配置来使能对应的中断位。 ```c /* Enable EOT, DXP, UDR, OVR, FRE, MODF and TSERF interrupts */ __HAL_SPI_ENABLE_IT(hspi, (SPI_IT_EOT | SPI_IT_DXP | SPI_IT_UDR | SPI_IT_OVR | SPI_IT_FRE | SPI_IT_MODF | SPI_IT_TSERF)); ``` | EOT | 传输结束 | | ----- | ------------------------------------------------------------ | | DXP | TXP(TXFIFO 中有空间可以放数据) 和 RXP(RXFIFO 中有空间可以放数据) 都有效 | | UDR | 下溢 | | OVR | 上溢 | | FRE | 帧格式错误 | | MODF | 模式故障 | | TSERF | 可重新装载 TSER | 通过上面的配置可以看到,如果 SPI 的 FIFO 设置为1 的情况下,那么就是每收发一个字节都会产生一个中断,虽然可以用,大数据高速率传输会导致中断响应太频繁,导致实时性下降。 3. DMA 模式 DMA 模式又分为循环模式和普通模式。 - 循环模式就是 DMA 自己不停的从 RAM 与 SPI DR 寄存器交换数据,每次收发数据完成都会产生一个完成中断,在考虑主从之间协议交互的时候,不能确定从机什么时候数据已经准备好了。如果主机发起的 CLK 读取的数据少于 DMA BUFF 的缓存区则会出现数据会继续在 RAM 地址之后放置。 - 普通模式每次使能 DMA 一次传输完成之后,需要用户再次使能 DMA 传输。从机可以在数据已经准备妥当之后,再激活 DMA 的传输,同时也可以通过其他的方式去通知到主机。再次启动 DMA 传输之后,从机接收的数据会从 DMA 缓存区的起始位置开始存放数据,方便数据的解析。 综上所述,本次实验主机使用轮询模式,从机使用 DMA 普通模式。 ### 三 ,SPI 的配置 本次实验借助 CUBEMX 工具来进行配置。 1. 使能 SPI 的时钟  将 SPI2 和 SPI4 的时钟源都配置为 100M 2. 对主机进行配置  设置 SPI 为全双工,使用软件 CS,8 个数据位,MSB,做了 256 分频是考虑到实验使用的是杜邦线,CPOL 和 CPHA 怎么配置无所谓,确保从机和主机一致即可。 3. SPI 双机配置  配置为从机模式,使用硬件 CS,其他配置保持和主机一致,因为从机的时钟由主机人提供,所以这里可以看到从机不需要配置时钟。   收发模式都设置为 DMA 普通传输模式。  使能外设中断和 DMA 中断。 4. 关闭 CACHE 为了保证 CACHE 对本次实验的影响,默认关闭 CACHE  ### 5. 实验代码 在编写实验代码之前,先梳理一下应该如何测试: 1. 使能外设(CUBEMX 配置会自动完成); 2. 启动 SPI 从机的 DMA 传输; 3. 主机读取数据; 4. 从机接收收据,接收完成后执行接收完成回调函数; 5. 校验数据。 ``` /* Initialize all configured peripherals */ MX_GPIO_Init(); MX_DMA_Init(); MX_SPI2_Init(); MX_SPI4_Init(); MX_UART4_Init(); /* Initialize interrupts */ MX_NVIC_Init(); /* USER CODE BEGIN 2 */ /* 拉高主机的 CS */ HAL_GPIO_WritePin(SPI2_CS_GPIO_Port, SPI2_CS_Pin, GPIO_PIN_SET); /* 设置从机的收发数据缓存区,长度为2 */ HAL_SPI_TransmitReceive_DMA(&hspi4, spi4_txbuff, spi4_rxbuff, 2); while (1) { /* 在从机 DMA 收发完成回调中对该标志位置 1 */ if (spi_flag) { spi_flag = 0; /* 对比 SP2 主机接收的数据与 SPI4 从机发送的数据是否一致 */ if(Buffercmp(spi2_rxbuff, spi4_txbuff, 2)) { master_error_num++; printf("SPI_MASTER error is %d\r\n",master_error_num); } /* 修改 SPI 从机发送缓存区中的数据 */ spi4_txbuff[0] ++; spi4_txbuff[1] += 2 ; /* 开启一轮新的传输 */ HAL_SPI_TransmitReceive_DMA(&hspi4, spi4_txbuff, spi4_rxbuff, 2); } else { /* 修改 SPI 主机发送数据 */ spi2_txbuff[0] ++; spi2_txbuff[1] += 2; /* 拉低片选 等待状态稳定后开始传输*/ HAL_GPIO_WritePin(SPI2_CS_GPIO_Port, SPI2_CS_Pin, GPIO_PIN_RESET); HAL_Delay(2); /* SPI 主机产生 CLK ,数据开始传输 */ HAL_SPI_TransmitReceive(&hspi2, spi2_txbuff, spi2_rxbuff, 2, 1000); /* 释放片选 */ HAL_GPIO_WritePin(SPI2_CS_GPIO_Port, SPI2_CS_Pin, GPIO_PIN_SET); } } void HAL_SPI_TxRxCpltCallback(SPI_HandleTypeDef *hspi) { if (hspi == &hspi4) { /* 对比 SPI2 主机发送的消息与 SPI4 从机接收的消息是否一致 */ if(Buffercmp(spi2_txbuff, spi4_rxbuff, 2)) { slave_error_num++; printf("SPI_SLAVE error is %d\r\n",slave_error_num); } spi_flag = 1; } } ``` 6. 注意事项 - 实验使用了 DMA,确保数据的缓存区是在 AXI RAM 区域段,也就是既地址是(0x2400 0000) 之后 - 在主机传输数据前,确保从机已经准备就绪 ## 总结 本文仅仅是双机通信的简单实现,只能验证 SPI 从机的功能正常,假想了一下 SPI 主从双机协议栈实现的需要考虑的问题点,希望有大佬能指点一下。 1. 从机如何获取 CS 的状态 主机在发起之前会拉低 CS,从机使用了硬件 CS,这个硬件 CS 不会产生中断通知到用户,时候需要从机单独设计一个引脚和 CS 连在一起来捕获这个下降沿。捕获这个下降沿有什么作用。 2. 主机获取从机的状态 从机数据是否准备就绪,从机提供一个 BUSY 引脚,主机通过读取 BUSY 引脚的状态来确定从机的状态,在高速传输过程中,从机也可以主动的通知到主机数据已经准备就绪,可以来读数据了。 3. 全双工模型的实现 SPI 是全双工的,可以同时收发数据,常见的场景如读写 FLASH,这些都是主从式的半双工模型,哪些场景下会真正的用到 SPI 的全双工,全双工是如何实现的。
6
条评论
默认排序
按发布时间排序
登录
注册新账号
关于作者
whj467467222
开源,分享,交流,共同进步
文章
32
回答
1222
被采纳
149
关注TA
发私信
相关文章
1
rtt的SPI作从机怎么编程?我尝试了,收不到数据!
2
请问基于rt-thread的spi slave (从机)应该怎么做
3
spi dma 中断接收的问题
4
rt_thread_delay中,关/开中断,导致SPI从机接收时数据丢失
5
freemodbus 从机 运行过程中怎么修改从机地址呢?
6
RT-Thread如何开启SPI中断
7
SPI配置为从模式,设备下电再上电,不能触发SPI中断
8
MCU采用PSI从设备通讯,发送数据到主设备,出现数据重复
9
Gd32 SPI从设备中断,有时无法收到数据,原因不明确
10
rt-thread的spi框架,rt_spi_transfer的详细功能细节理解
推荐文章
1
RT-Thread应用项目汇总
2
玩转RT-Thread系列教程
3
国产MCU移植系列教程汇总,欢迎查看!
4
机器人操作系统 (ROS2) 和 RT-Thread 通信
5
五分钟玩转RT-Thread新社区
6
【技术三千问】之《玩转ART-Pi》,看这篇就够了!干货汇总
7
关于STM32H7开发板上使用SDIO接口驱动SD卡挂载文件系统的问题总结
8
STM32的“GPU”——DMA2D实例详解
9
RT-Thread隐藏的宝藏之completion
10
【ART-PI】RT-Thread 开启RTC 与 Alarm组件
热门标签
RT-Thread Studio
串口
Env
LWIP
SPI
AT
Bootloader
Hardfault
CAN总线
FinSH
ART-Pi
DMA
USB
文件系统
RT-Thread
SCons
RT-Thread Nano
线程
MQTT
STM32
RTC
rt-smart
FAL
I2C_IIC
UART
ESP8266
cubemx
WIZnet_W5500
ota在线升级
PWM
BSP
flash
freemodbus
packages_软件包
潘多拉开发板_Pandora
定时器
ADC
GD32
flashDB
socket
编译报错
中断
Debug
rt_mq_消息队列_msg_queue
keil_MDK
ulog
SFUD
msh
C++_cpp
MicroPython
本月问答贡献
RTT_逍遥
10
个答案
3
次被采纳
xiaorui
3
个答案
2
次被采纳
winfeng
2
个答案
2
次被采纳
三世执戟
8
个答案
1
次被采纳
KunYi
8
个答案
1
次被采纳
本月文章贡献
catcatbing
3
篇文章
5
次点赞
lizimu
2
篇文章
8
次点赞
swet123
1
篇文章
4
次点赞
Days
1
篇文章
4
次点赞
YZRD
1
篇文章
2
次点赞
回到
顶部
发布
问题
投诉
建议
回到
底部