当前位置: 首页 > news >正文

【通信协议】SPI总线

目录

SPI简介

硬件电路

SPI模式

软件模拟初始化 

时序基本单元

起始条件

终止条件

发送与接收

 SPI基本单元代码 

MySPI.h

MySPI.c  


SPI简介

SPI(Serial Peripheral Interface),即串行外围设备接口,是由Motorola公司开发的一种高速全双工通用数据总线。它被广泛地使用在要求通讯速率较高的场合。

特征

  • 同步,全双工,串行
  • 通讯速率较高
  • 支持总线挂载多设备(一主多从)
  • 四条通信线
  • 短距离

通信线

SCK(Serial Clock):串行时钟线,主机生成时钟,主机和从机根据电平边沿(上升沿或下降沿)发送数据位、接收数据位

MOSI(Master Output Slave Input):主机输出,从机输入

MISO(Master Input Slave Output):主机输入,从机输出

SS(Slave Select):指定主机与哪个从机通信,同一时间主机只能选择一个从机,故只能设置一个SS低为电平。未被选中(NSS引脚为高电平)的从设备会忽略总线上的数据传输

传输速度

SPI总线上的主设备必须在通信开始时候配置并生成相应的时钟信号。从理论上讲,只要实际可行,时钟速率就可以是你想要的任何速率,当然这个速率受限于每个系统能提供多大的系统时钟频率,以及最大的速率。

硬件电路

所有SPI设备的SCK、MOSI、MISO分别连在一起

主机另外引出多条SS控制线,分别接到各从机的SS引脚

输出引脚配置为推挽输出,输入引脚配置为浮空或上拉输入

当从机的SS引l脚为高电平,也就是从机未被选中时,它的MISO引脚,必须切换为高阻态,在SS为低电平时,MISO才允许变为推挽输出。在SS未被选中的状态,从机的MISO引脚必须关断输出,即配置输出为高阻态。

SPI模式

SPI 根据时钟极性和时钟相位的不同可以有4种工作模式,通常在编写 SPI 设备驱动程序时需要特别注意从设备的时钟相位与时钟极性,如果与主设备不一致时,一般都不能进行正常的通信

  1. 模式0(CKPL=0,CKPH=0):

    • 时钟极性(Clock Polarity)为0,表示时钟空闲状态为低电平。
    • 时钟相位(Clock Phase)为0,表示数据在时钟信号的奇数边沿(时钟上升沿)进行采样和稳定。
  2. 模式1(CKPL=0,CKPH=1):

    • 时钟极性为0,时钟空闲状态为低电平。
    • 时钟相位为1,数据在时钟信号的偶数边沿(时钟下降沿)进行采样和稳定。
  3. 模式2(CKPL=1,CKPH=0):

    • 时钟极性为1,时钟空闲状态为高电平。
    • 时钟相位为0,数据在时钟信号的奇数边沿(时钟下降沿)进行采样和稳定。
  4. 模式3(CKPL=1,CKPH=1):

    • 时钟极性为1,时钟空闲状态为高电平。
    • 时钟相位为1,数据在时钟信号的偶数边沿(时钟上升沿)进行采样和稳定。

软件模拟初始化 

根据SPI硬件规定:

MISO(Master Input Slave Output),SCK(Serial Clock),SS(Slave Select)均配置为推挽输出

MISO(Master Input Slave Output)配置为上拉输入

GPIO的其它参数的理解可以阅读下方博客,这里不再赘述。

【STM32】GPIO和AFIO标准库使用框架_gpio afio-CSDN博客

本篇软件模拟使用SPI的模式0 

void MySPI_W_SS(uint8_t BitValue)
{GPIO_WriteBit(GPIOA, GPIO_Pin_4, (BitAction)BitValue);
}void MySPI_W_SCK(uint8_t BitValue)
{GPIO_WriteBit(GPIOA, GPIO_Pin_5, (BitAction)BitValue);
}void MySPI_W_MOSI(uint8_t BitValue)
{GPIO_WriteBit(GPIOA, GPIO_Pin_7, (BitAction)BitValue);
}uint8_t MySPI_R_MISO(void)
{return GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_6);
}void MySPI_Init(void)
{RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_7;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIO_InitStructure);//推挽输出默认输出低电平MySPI_W_SS(1);//不选择从机,空闲状态MySPI_W_SCK(0);//模式0空闲时的SCKGPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIO_InitStructure);}

时序基本单元

起始条件

SS(Slave Select)从高电平切换到低电平

void MySPI_Start(void)
{
//初始化时已经配置空闲状态时为高电平,现拉低选择从机MySPI_W_SS(0);
}

终止条件

SS从低电平切换到高电平

void MySPI_Stop(void)
{
//通讯过程中,SS为低电平,拉高代表通讯结束MySPI_W_SS(1);
}

发送与接收

SPI只有主模式和从模式之分,没有读和写的说法,外设的写操作和读操作是同步完成的。如果只进行写操作,主机只需忽略接收到的字节;反之,若主机要读取从机的一个字节,就必须发送一个空字节来引发从机的传输。也就是说,你发一个数据必然会收到一个数据;你要收一个数据必须也要先发一个数据。

硬件SPI,主机SCK电平变化与数据读写几乎同时进行,但软件SPI,程序是一条一条执行的,软件模拟就先变化时钟,再读写数据位,不是同时进行的

交换一个字节(模式0)

CPOL=0:空闲状态时,SCK为低电平

CPHA=0:SCK第一个边沿移入数据,第二个边沿移出数据

在SS下降沿的时候,主机和从机就需要移出数据,那样SCK的第一个边沿才能移入数据

uint8_t MySPI_SwapByte(uint8_t ByteSend)
{uint8_t i, ByteReceive = 0x00;for (i = 0; i < 8; i ++){MySPI_W_MOSI(ByteSend & (0x80 >> i));//第一次在SS下降沿时发送数据位,之后SCK下降沿时发送数据位MySPI_W_SCK(1);if (MySPI_R_MISO() == 1){ByteReceive |= (0x80 >> i);}//SCK上升沿时接收数据位MySPI_W_SCK(0);}//SCK的同步时钟指导读写数据的一位,完成一个字节的交换return ByteReceive;
}

 SPI基本单元代码 

MySPI.h

#ifndef __MYSPI_H
#define __MYSPI_Hvoid MySPI_Init(void);
void MySPI_Start(void);
void MySPI_Stop(void);
uint8_t MySPI_SwapByte(uint8_t ByteSend);#endif

MySPI.c  

#include "stm32f10x.h"                  // Device headervoid MySPI_W_SS(uint8_t BitValue)
{GPIO_WriteBit(GPIOA, GPIO_Pin_4, (BitAction)BitValue);
}void MySPI_W_SCK(uint8_t BitValue)
{GPIO_WriteBit(GPIOA, GPIO_Pin_5, (BitAction)BitValue);
}void MySPI_W_MOSI(uint8_t BitValue)
{GPIO_WriteBit(GPIOA, GPIO_Pin_7, (BitAction)BitValue);
}uint8_t MySPI_R_MISO(void)
{return GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_6);
}void MySPI_Init(void)
{RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_7;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIO_InitStructure);//推挽输出默认输出低电平MySPI_W_SS(1);MySPI_W_SCK(0);GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIO_InitStructure);}void MySPI_Start(void)
{MySPI_W_SS(0);
}void MySPI_Stop(void)
{MySPI_W_SS(1);
}uint8_t MySPI_SwapByte(uint8_t ByteSend)
{uint8_t i, ByteReceive = 0x00;for (i = 0; i < 8; i ++){MySPI_W_MOSI(ByteSend & (0x80 >> i));MySPI_W_SCK(1);if (MySPI_R_MISO() == 1){ByteReceive |= (0x80 >> i);}MySPI_W_SCK(0);}return ByteReceive;
}

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • JVM - 1.类加载子系统
  • 工业相机错峰启动优化方案
  • 【最长公共子序列】
  • 程序员的日常挑战:如何在编码工作与持续学习之间找到平衡?
  • 电子克隆方法的优缺点有哪些?
  • 计数排序算法及优化(java)
  • 搜狐新闻HarmonyOS Push开发实践
  • 火绒安全:一款强大且高效的国产杀毒软件
  • C语言基础(十二)
  • kubernetest中wait.Until()方法的源码解读
  • 《黑神话·悟空》是用什么编程语言开发的?
  • 豆包大模型升级:日均Tokens使用量破5000亿,字节跳动打造即刻体验的《Her》式AI
  • yield生成器生成表单字段,并进行验证,利用fetch方法和formData对象传递数据给后端,后端返回成功,返回数据
  • LambdaQueryWrapper 是 MyBatis-Plus超级利器
  • Telegram mini app 本地开发配置
  • 分享的文章《人生如棋》
  • $translatePartialLoader加载失败及解决方式
  • ABAP的include关键字,Java的import, C的include和C4C ABSL 的import比较
  • canvas绘制圆角头像
  • es6--symbol
  • jdbc就是这么简单
  • js对象的深浅拷贝
  • linux安装openssl、swoole等扩展的具体步骤
  • Netty 框架总结「ChannelHandler 及 EventLoop」
  • Travix是如何部署应用程序到Kubernetes上的
  • TypeScript迭代器
  • 程序员该如何有效的找工作?
  • 初识 beanstalkd
  • 给Prometheus造假数据的方法
  • 官方新出的 Kotlin 扩展库 KTX,到底帮你干了什么?
  • 汉诺塔算法
  • 探索 JS 中的模块化
  • 小李飞刀:SQL题目刷起来!
  • 主流的CSS水平和垂直居中技术大全
  • 【运维趟坑回忆录 开篇】初入初创, 一脸懵
  • Unity3D - 异步加载游戏场景与异步加载游戏资源进度条 ...
  • 曜石科技宣布获得千万级天使轮投资,全方面布局电竞产业链 ...
  • # Java NIO(一)FileChannel
  • #if和#ifdef区别
  • #预处理和函数的对比以及条件编译
  • $forceUpdate()函数
  • (react踩过的坑)antd 如何同时获取一个select 的value和 label值
  • (二)【Jmeter】专栏实战项目靶场drupal部署
  • (六)Hibernate的二级缓存
  • (七)Flink Watermark
  • (四)搭建容器云管理平台笔记—安装ETCD(不使用证书)
  • (五)c52学习之旅-静态数码管
  • (转)visual stdio 书签功能介绍
  • (转)使用VMware vSphere标准交换机设置网络连接
  • . ./ bash dash source 这五种执行shell脚本方式 区别
  • .NET C# 使用GDAL读取FileGDB要素类
  • .Net 访问电子邮箱-LumiSoft.Net,好用
  • .NET应用UI框架DevExpress XAF v24.1 - 可用性进一步增强
  • @JsonFormat与@DateTimeFormat注解的使用
  • [ vulhub漏洞复现篇 ] Hadoop-yarn-RPC 未授权访问漏洞复现