news 2026/11/6 0:28:58

硬件SPI和软件模拟SPI在配置方面的一点区别

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
硬件SPI和软件模拟SPI在配置方面的一点区别

一 硬件SPI和软件SPI

顾名思义这种方式是硬件层面的,需要开启并使用单片机里面的SPI外设,IO口要复用到外设,由外设来控制电平翻转;软件模拟就可以随便选择IO口进行配置,需要手动控制电平翻转

硬件接线:

我使用的是外接Flash模块无论是硬件还是软件接线都使用的是相同的IO口

对应关系:
A4-CS
A5-SCK时钟线
A6-MISO-D0
A7-MOSI-D1 接线口诀:主机MOSI接从机DI 主机 MISO接从机DO

二 配置GPIO

配置IO文字描述:

配置GPIO口
把CS SCK MOSI配置为推挽输出
把MISO配置为浮空输入
默认CS高电平抬起不选中状态

硬件版本

void W25Q_SPI_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct;
SPI_InitTypeDef SPI_InitStruct;

// 开启时钟:GPIOA、SPI1(SPI1属于APB2)
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_SPI1, ENABLE);

// PA5 SCK、PA7 MOSI:复用推挽输出 AF_PP
GPIO_InitStruct.GPIO_Pin = SPI_SCK_PIN | SPI_MOSI_PIN;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(SPI_GPIO_PORT, &GPIO_InitStruct);

// PA6 MISO:浮空输入
GPIO_InitStruct.GPIO_Pin = SPI_MISO_PIN;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(SPI_GPIO_PORT, &GPIO_InitStruct);

// PA4 CS:普通推挽输出
GPIO_InitStruct.GPIO_Pin = FLASH_CS_PIN;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(FLASH_CS_PORT, &GPIO_InitStruct);
FLASH_CS_H(); // 默认片选拉高,不选中Flash

// SPI配置
SPI_InitStruct.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
SPI_InitStruct.SPI_Mode = SPI_Mode_Master;
SPI_InitStruct.SPI_DataSize = SPI_DataSize_8b;
SPI_InitStruct.SPI_CPOL = SPI_CPOL_Low; // CPOL=0 空闲SCK低
SPI_InitStruct.SPI_CPHA = SPI_CPHA_1Edge; // CPHA=0 第一个边沿采样(Mode0)
SPI_InitStruct.SPI_NSS = SPI_NSS_Soft; // 软件管理片选
SPI_InitStruct.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_4; //72M/4=18M
SPI_InitStruct.SPI_FirstBit = SPI_FirstBit_MSB; //高位先发,Flash强制要求
SPI_InitStruct.SPI_CRCPolynomial = 7;

SPI_Init(SPI1, &SPI_InitStruct); //硬件SPI必须调用SPI_Init
SPI_Cmd(SPI1, ENABLE); //使能SPI外设
}

软件模拟版本

//软件模拟SPI初始化
void W25Q_SPI_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct;
// 只开启GPIOA时钟,删掉SPI1时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);

// SCK PA5、MOSI PA7:推挽输出
GPIO_InitStruct.GPIO_Pin = SPI_SCK_PIN | SPI_MOSI_PIN;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(SPI_GPIO_PORT, &GPIO_InitStruct);

// MISO PA6:浮空输入
GPIO_InitStruct.GPIO_Pin = SPI_MISO_PIN;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(SPI_GPIO_PORT, &GPIO_InitStruct);

// CS PA4:推挽输出
GPIO_InitStruct.GPIO_Pin = FLASH_CS_PIN;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(FLASH_CS_PORT, &GPIO_InitStruct);

FLASH_CS_H(); // 默认片选拉高

}

三 收发字节函数的区别

收发字节文字描述:

发送一字节数据
函数有返回值因为是全双工 有发送就有接收
要发送的数据作为函数参数传入
通过循环从最高位开始发送数据
每次都要提取出最高位数据进行发送
然后发送数据左移一位
选择模式0 就是上升沿采样
SCK输出高电平,保持不变
读取MISO电平,进行或操作拼接接收电平数据
SCK输出低电平 停止采样
保持一段时间后,进行下一轮数据的发送和读取

硬件版本

uint8_t SPI_RW_Byte(uint8_t txDat)
{
//等待发送缓冲区为空
while(SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET);
SPI_I2S_SendData(SPI1, txDat);

//等待接收完成
while(SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) == RESET);
return SPI_I2S_ReceiveData(SPI1);
}

软件模拟版本

uint8_t SPI_RW_Byte(uint8_t txdata)
{
uint8_t i;
uint8_t rxData = 0;

for(i = 0;i<8;i++){
if(txdata & 0x80){
GPIO_SetBits(SPI_GPIO_PORT, SPI_MOSI_PIN);
}else{
GPIO_ResetBits(SPI_GPIO_PORT, SPI_MOSI_PIN);
}
txdata <<= 1;

GPIO_SetBits(SPI_GPIO_PORT, SPI_SCK_PIN);
delay_us(2);//延时函数可以去掉 我加上是为了健壮性

rxData <<= 1;
if(GPIO_ReadInputDataBit(SPI_GPIO_PORT, SPI_MISO_PIN)){
rxData |= 0x01;
}
GPIO_ResetBits(SPI_GPIO_PORT, SPI_SCK_PIN);
delay_us(2);//同理,为了健壮性
}
return rxData;
}

这个SPI手写时序虽然最开始也是参照AI和现成代码写出来的,但是我会一行一行的理解,然后独立参照datasheet写出来,刚开始接触的时候确实感觉很难,但现在感觉好一点了

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/29 16:27:17

如何构建浏览器扩展专用CLI:从impeccable误读到zcode实战

1. 项目概述&#xff1a;一个被误读的 CLI 工具名&#xff0c;以及它背后真实的工程实践逻辑“impeccable”这个词在英语里意思是“无懈可击的、无可挑剔的”&#xff0c;常用来形容工艺、服务或表现达到极致水准。但当它突然出现在技术热搜榜上&#xff0c;和npx、CLI、browse…

作者头像 李华
网站建设 2026/10/30 11:09:05

计算机专业AI工具链:CLI驱动的VS Code智能工作流

1. 这不是“AI入门课”&#xff0c;而是计算机专业学生的生存技能补丁你刚在实验室调试完一个跑不通的MPI并行程序&#xff0c;导师甩来一句&#xff1a;“试试用AI帮你看下死锁在哪。”你打开VS Code&#xff0c;想装个能写Python的插件&#xff0c;搜“AI”跳出二十个叫“Cla…

作者头像 李华
网站建设 2026/10/31 10:38:45

AI论文写作的真实感如何实现?虎贲等考AI全流程实测

每年一到毕业季&#xff0c;后台就会塞满同款问题&#xff1a;写论文软件哪个好&#xff1f;手上一堆AI工具&#xff0c;选题不会提、框架写不出来、文献列表看着有模有样&#xff0c;一点开全是编的&#xff0c;最气人的是答辩前一晚发现重复率比身高还高。这两年我也把市面上…

作者头像 李华
网站建设 2026/10/31 7:24:08

三菱伺服扭矩控制原理与实战调试指南

1. 扭矩控制不是“调个参数就完事”&#xff1a;先搞清它到底在控制什么 很多人一看到“三菱伺服电机扭矩控制”&#xff0c;第一反应就是翻手册、找Pr.100、Pr.101这类参数编号&#xff0c;然后照着数值填进去——结果电机要么不动&#xff0c;要么抖得像筛糠&#xff0c;或者…

作者头像 李华
网站建设 2026/11/1 8:06:12

如何给Claude装上长期记忆:AI辅助开发的外挂记忆系统实战

为什么我需要给 Claude 装上“长期记忆”做 AI 辅助开发时间长了&#xff0c;你一定遇到过这种场景&#xff1a;上午还在终端里和 Claude 聊一个模块的接口设计&#xff0c;聊到一半去开周会&#xff0c;下午回来继续时&#xff0c;它完全不记得上午的结论。上个月排查过一个诡…

作者头像 李华
网站建设 2026/10/31 15:39:51

C#上位机必须掌握的CAN DBC解析器:从原理到工程实现

简介&#xff1a;一份面向汽车电子与嵌入式开发者的CAN DBC文件解析查看工具&#xff0c;内置C#完整工程源码&#xff0c;适合需要解析DBC报文、信号定义以及进行定制化二次开发的工程师学习使用。工具支持通过图形界面加载DBC文件&#xff0c;直观展示节点、报文、信号、属性等…

作者头像 李华