dffzh 发表于 2025-4-2 09:01

使用普通GPIO实现模拟SPI功能

本帖最后由 dffzh 于 2025-4-2 16:19 编辑

#申请原创#
@21小跑堂



在MCU硬件资源(包括总线资源和引脚资源等)受限时,如果需要使用SPI总线进行MCU和外围器件的通信,在硬件SPI损坏或缺失的情况下,可以考虑使用软件模拟SPI总线,即通过普通的GPIO引脚的电平翻转和延时操作实现SPI数据的读取和写入。以下实现模拟SPI的功能已在AT32F403ACCT7平台上成功验证。首先你需要对4个GPIO引脚进行普通输入输出模式的配置,即CS信号-PB6脚,CLK信号-PB7脚,MOSI信号-PB9脚,MISO信号-PB8脚,如下图所示:其次你需要实现数据的读取和写入操作,我的这个demo例程是通过IS3980S芯片实现数字量信号的读取操作,即MCU和IS3980S之间以模拟SPI方式实现数据读写操作,所以变量等的命名基本上都带有“IS3980S”字符串,但这毫无影响,以下是GPIO的定义实现:对于MCU输出的引脚,定义了输出高电平或低电平的宏;对于MCU输入的引脚,定义了读取外部电平信号的宏。
另外,定义了非精确的延时操作函数和延时时间,如下图:
IS3980S_DELAY_TIME为延时时间,决定了模拟SPI的时钟频率,其值越小,时钟频率越大,而能支持的最大时钟频率由MCU主频决定。在实际项目开发时,需要通过使用示波器抓取CLK信号的波形来判断波形失真情况,以及根据硬件上设计的RC滤波电路来计算截止频率,加上产品规格需求,基本上就能判断出来模拟SPI设计是否能符合项目需求。

对于读取函数,即MCU从外围器件读取数据,主要原理是在操作CS信号为低电平期间,通过控制CLK信号并在其上升沿或下降沿时刻(demo为上升沿,参考器件手册)读取MISO信号的电平,并存储到8位无符号变量read_byte里面,主要代码如下所示 :读取函数的返回值read_byte即为读取到的一个字节数据。
对于写入函数,即MCU向外围器件写入数据,函数入参write_byte即为需要写入的一个字节数据,主要原理是在操作CS信号为低电平期间,通过控制CLK信号并在其上升沿或下降沿时刻(demo为上升沿,参考器件手册)输出MOSI为高电平或低电平,主要代码如下所示:模拟SPI应用场景很多,相对于硬件SPI,以下对两者的优缺点进行了简单说明,大家可以根据实际应用场景和需求进行选择。模拟SPI优点:移植性强:如以上代码所示,一般只需要修改底层驱动配置,即可在其他MCU上使用;灵活性高:普通GPIO即可实现,在SPI总线资源不够时可以考虑。缺点:处理器负载高:需要频繁地控制GPIO电平翻转;传输速度慢:受MCU主频和GPIO电平翻转速度影响。
硬件SPI优点:传输速度快:能够达到几十MHz的速度,适合实时性要求较高的场合;处理器负载低:使用硬件模块进行通信,执行效率高。缺点:成本较高:需要专门的硬件模块,增加成本;灵活性较低:硬件SPI模块损坏后,就无法实现SPI总线通信。
   

dffzh 发表于 2025-4-2 15:55

@21小跑堂
申请原创,请审核。

呐咯密密 发表于 2025-4-2 16:47

spi还是硬件好,软件太鸡肋了

qinlu123 发表于 2025-4-2 17:59

本帖最后由 qinlu123 于 2025-4-2 18:03 编辑




既然申请原创程序写的就不能再像个刚毕业的大学生,你把SPI驱动和上层的设备驱动都揉一块去了,设想一下如果还有一个SPI设备也要挂载在你现在的总线上你程序应该怎么改。

qinlu123 发表于 2025-4-2 18:05


dffzh 发表于 2025-4-3 08:47

本帖最后由 dffzh 于 2025-4-3 10:16 编辑

qinlu123 发表于 2025-4-2 17:59
既然申请原创程序写的就不能再像个刚毕业的大学生,你把SPI驱动和上层的设备驱动都揉一块去了,设想一下 ...
阁下的解耦操作,值得学习;
本文章主要是以编写外围芯片IS3980S驱动的方式实现模拟SPI功能.

justin0407 发表于 2025-4-3 15:16

给模拟SPI点赞,有时候SPI接口不够用可以参考这个方法。

dffzh 发表于 2025-4-3 16:34

justin0407 发表于 2025-4-3 15:16
给模拟SPI点赞,有时候SPI接口不够用可以参考这个方法。

是的,也是间接实现外围器件芯片驱动程序的方法

wwppd 发表于 2025-4-4 14:57

在发送和接收数据时,需要适当的延时以确保数据稳定。

adolphcocker 发表于 2025-4-4 15:43

建议在关键部分关闭中断,以确保时序不受干扰。

burgessmaggie 发表于 2025-4-4 17:14

时序控制、电平匹配、抗干扰设计。

dffzh 发表于 2025-4-5 10:16

wwppd 发表于 2025-4-4 14:57
在发送和接收数据时,需要适当的延时以确保数据稳定。

是的,需要考虑

dffzh 发表于 2025-4-5 10:17

adolphcocker 发表于 2025-4-4 15:43
建议在关键部分关闭中断,以确保时序不受干扰。

是的,可以结合实际应用代码通过关全局中断操作

dffzh 发表于 2025-4-5 10:18

burgessmaggie 发表于 2025-4-4 17:14
时序控制、电平匹配、抗干扰设计。

是的,代码不仅需要考虑功能,也需要考虑性能{:handshake:}

caigang13 发表于 2025-4-5 11:04

理论上IO口都可以模拟常用的外设,但是效率太低了。

chenjun89 发表于 2025-4-5 16:31

用普通IO模拟SPI通信,效率有点低了。

bartonalfred 发表于 2025-4-5 19:08

在布线时,尽量减少信号传输线的长度和干扰,避免信号反射和串扰。

dffzh 发表于 2025-4-5 20:05

caigang13 发表于 2025-4-5 11:04
理论上IO口都可以模拟常用的外设,但是效率太低了。

是的,模拟I2C和模拟UART等都可以实现,效率上肯定没有标准总线高,但在有些对速率要求不高的场合,还是有用武之地的

dffzh 发表于 2025-4-5 20:06

bartonalfred 发表于 2025-4-5 19:08
在布线时,尽量减少信号传输线的长度和干扰,避免信号反射和串扰。

是的,PCB信号线走线也是比较有讲究的{:handshake:}

dffzh 发表于 2025-4-5 20:06

chenjun89 发表于 2025-4-5 16:31
用普通IO模拟SPI通信,效率有点低了。

效率上肯定没有标准总线高,但在有些对速率要求不高的场合,还是有用武之地的
页: [1] 2 3 4
查看完整版本: 使用普通GPIO实现模拟SPI功能