SPI协议代码

SPI协议代码软件模拟SPI程序代码概述:   通过两个MCU(STM32F103)来模拟SPI的主从机,完成主机发送从机接收,便于理解SPI协议。SPI协议简介●SPI接口介绍  SCK:时钟信号,由主设备产生,所以主设备SCK信号为输出模式,从设备的SCK信号为输入模式。  CS:使能信号,由主设备控制从设备,,所以主设备CS信号为输出模式,从设备的CS信号为输入模式。  MOSI:主设备数据输出,从设备数据输入,所以主设备MOSI信号为输出模式,从设备的MOSI信号为输入模式。  MISO:主设备数

大家好,又见面了,我是你们的朋友全栈君。如果您正在找激活码,请点击查看最新教程,关注关注公众号 “全栈程序员社区” 获取激活教程,可能之前旧版本教程已经失效.最新Idea2022.1教程亲测有效,一键激活。

Jetbrains全系列IDE稳定放心使用

软件模拟SPI程序代码


概述:
   通过两个MCU(STM32F103)来模拟SPI的主从机,完成主机发送从机接收,便于理解SPI协议。

SPI协议简介

●SPI接口介绍

  SCK: 时钟信号,由主设备产生,所以主设备SCK信号为输出模式,从设备的SCK信号为输入模式。
  CS: 使能信号,由主设备控制从设备,,所以主设备CS信号为输出模式,从设备的CS信号为输入模式。
  MOSI: 主设备数据输出,从设备数据输入,所以主设备MOSI信号为输出模式,从设备的MOSI信号为输入模式。
  MISO: 主设备数据输入,从设备数据输出,所以主设备MISO信号为输入模式,从设备的MISO信号为输出模式。
  
SPI接口连接图
在这里插入图片描述
  注意:MOSI和MISO不能交叉连接(可以把主从机理解为一个整体系统,MOSI为系统主机发送从机接收的数据线,MISO为主机接收从机发送的数据线)。

●SPI数据传输方向

SPI作为全双工的的串行通信协议,数据传输时高位在前,低位在后。主机和从机公用由主机产生的SCK信号,所以在每个时钟周期内主机和从机有1bit的数据交换(因为MOSI和MISO数据线上的数据都是在时钟的边沿处被采样)。
  如下图:
 在这里插入图片描述
 SPI协议规定数据采样是在SCK的上升沿或下降沿时刻(由SPI模式决定,下面会说到),观察上图,在SCK的边沿处(上升沿或下降沿),主机会在MISO数据线上采样(接收来从机的数据),从机会在MOSI数据线上采样(接收来自主机的数据),所以每个时钟周期中会有一bit的数据交换。
 SPI数据交换

●SPI传输模式

SPI总线传输一共有4种模式,这4种模式分别由时钟极性(CPOL)和时钟相位(CPHA)来定义。
   在这里插入图片描述

CPOL CPHA
规定了SCK时钟信号空闲状态的电平 规定了数据是在SCK时钟的上升沿还是下降沿被采样
———– ————————————
模式0:CPOL=0,CPHA =0 SCK空闲为低电平,数据在SCK的上升沿被采样(提取数据)
模式1:CPOL=0,CPHA =1 SCK空闲为低电平,数据在SCK的下降沿被采样(提取数据)
模式2:CPOL=1,CPHA =0 SCK空闲为高电平,数据在SCK的下降沿被采样(提取数据)
模式3:CPOL=1,CPHA =1 SCK空闲为高电平,数据在SCK的上升沿被采样(提取数据)

以模式0为例:
SCK空闲为低电平,数据在SCK的上升沿被采样(提取数据),在SCK的下降沿切换数据线的数据。
在这里插入图片描述
  ◐在时钟的第1个上升沿(游标1处)(采样点)
  MOSI上数据为1,则在此边沿从机采样(提取)数据为1,采样点在MOSI数据线的中间。
  MISO上数据为0,则在此边沿主机采样(提取)数据为0,采样点在MISO数据线的中间。
  ◐在时钟的第1个下降沿(游标2处)(切换点)
  MOSI上数据由1切换为0,,数据在时钟下降沿时切换数据。
  MISO上数据由0切换为1,,数据在时钟下降沿时切换数据。
 ◐在时钟的第2~8个上升沿(采样点),主机在MISO上采样数据,从机在MOSI上采样数据。
 ◐在时钟的第2~8个下降沿(切换点),主机在MISO上切换数据,从机在MOSI上切换数据。

通过两个单片机模拟SPI来加深理解

利用了STM32F103VET6和STM32F103C8T6(身边只有这两块了)两款MUC。

※硬件连接方式

主机- STM32F103VET6 从机-STM32F103C8T6
(主机产生) SCK→ →SCK(从机被动)
(主机产生) CS→ →CS (从机被动)
(主机发送)MOSI → →MOSI (从机接收)
(主机接收) MISO ← ←MISO (从机发送)

●注意:MOSI连接MOSI,MISO连接MISO(不能像串口那样交叉连接)。

✯SPI模式

采用模式0(CPOL=0,CPHA =0):SCK空闲为低电平,数据在SCK的上升沿被采样(提取数据) ,在SCK的下降沿被切换。

✯程序思路

★主机拉低CS开始传输数据,在SCK上升沿之前保持MOSI上有稳定的数据输出(因为从机要在SCK的上升沿去采样(提取数据),所以主机在SCK上升沿之前要完成发送数据的放置)。
  ★从机在CS拉低后(CS有下降沿)开始数据的接收(在SCK的上升沿采集MOSI上的数据)。

✯主机C代码+波形

/*SPI发送函数*/
//时钟的上升沿采样数据,下降沿切换数据 先发送高位
void SPI_Write(uint8_t Data)
{ 
   
	uint8_t i=0;
	CS_L;	//片选拉低开始传输数据
	/*循环8次,发送8bit数据*/
	for(i=0;i<8;i++)	
	{ 
   
		
		/*切换数据*/
		if(Data&0x80)//通过8次循环移位,将一个字节的数据,由高到低一位一位的放置到数据线上
		{ 
   
			MOSI_H;
		}
		else
		{ 
   
			MOSI_L;
		}
		SCK_L;//产生下降沿,准备切换数据
		delay_us(1);//(可忽略,这里是因为接收时此单片机外部中断上升沿触发有时延,SCK太快无法准确提取数据)
		SCK_H;	//产生上升沿(从机在此上升沿时采集数据)
		Data <<= 1;
		
	}
	MOSI_L;
	SCK_L;
	CS_H;	//片选拉高等待下次数据传输
}
int main()
{ 
   
	int i=0,j=0;
	SysTick_init();
	SPI_GPIO_Config();
	while(1)
	{ 
   
		SPI_Write(0xA5);
	}
}

●注意:上面1us的延时[delay_us(1)]此处可以忽略,这里是因为接收时此单片机外部中断上升沿触发有时延,SCK太快无法准确提取数据,利用其他方式解析从机数据的请忽略。(详细了解请参考博文:STM32外部中断边沿触发存在延时问题)。
  ★代码解析:要了解代码思路,就要时刻记得我们采用SPI的是模式0(SCK空闲为低电平,数据在SCK的上升沿被采样(提取数据),下降沿被切换 ),所以1Byte数据放置完毕后,SCK要拉低,CS要拉高,MOSI要恢复默认电平,但是每Bit数据在SCK拉低时被放置到MOSI数据线(因为SCK上升沿前要确保稳定的数据(因为接收数据最好是在数据的中间采样),这样从机才可以在上升沿采样到正确的数据)。所谓放置数据,其实就是在每次SCK拉高之前对MOSI引脚赋值。比如我们发送的数据为0xA5(1010_0101)。
在这里插入图片描述
在上图中标号2处的下降沿处切换数据,上升沿之前保证了MOSI上(游标1)处有稳定的1bit数据(1),随后的7个上升沿也一样分析。
✯主机产生的波形
在这里插入图片描述)
  ★波形解析:通道1数据:SCK
        通道2数据:MOSI
        通道3数据:CS
   在上图中可以观察到整个数据的传输是在片选CS为低的时刻进行的。在SCK下降沿时主机对MOSI数据线上的数据进行了切换,在SCK上升沿之前完成了1bit数据的发送。完成1Byte数据的发送后,SC置高,CS置高,MOSI置低,为下一帧数据做准备。

✯从机C代码+波形

/*SPI接收数据*/
uint8_t SPI_Read()
{ 
   
	/*CS下降沿*/
	if(CS_Trigger_Falling == 1)
	{ 
   
		CS_Trigger_Falling = 0;
		/*SCK上升沿*/
		for(i=0;i<8;i++)
		{ 
   
			while(SCK_Trigger_Rising != 1);//等待上升沿
			SCK_Trigger_Rising = 0;
			Data_Rec<<=1;
			if(MOSI_State)//在SCK上升沿时提取数据
			{ 
   		
				Data_Rec ++;	
				Rec_Data1[i] = 1;	
			}
			else{ 
   }
		}
	}
	return Data_Rec;
}
int main()
{ 
   
	SysTick_init();
	SPI_GPIO_Config();
	EXTI_PB1_Config();
	EXTI_PA2_Config();
	while(1)
	{ 
   
	Get_Data = SPI_Read();

	}
}
/*外部中断0中断*/
void EXTI1_IRQHandler(void)//中断服务函数
{ 
   
	if(EXTI_GetITStatus(EXTI_Line1) != RESET )//reset为清零(!=reset等价于IT=1)
	{ 
   
	SCK_Trigger_Rising = 1;
	EXTI_ClearITPendingBit(EXTI_Line1);
	}


}
void EXTI2_IRQHandler(void)//中断服务函数
{ 
   
	if(EXTI_GetITStatus(EXTI_Line2) != RESET )//reset为清零(!=reset等价于IT=1)
	{ 
   
	CS_Trigger_Falling = 1;
	EXTI_ClearITPendingBit(EXTI_Line2);
	
	}


}

★代码解析:从机采用了外部中断的方式去采集CS的下降沿和SCK的上升沿(从机以CS下降沿为数据接收的开始,以SCK的上升沿作为每bit数据的采样点)。(★★★有好的方法欢迎指导)
  ●CS下降沿提取波形:图中紫色信号为CS下降沿点。
  在这里插入图片描述
  ●SCK上升沿提取波形:下图中紫色信号为SCK上升沿的提取(即从机接收MOSI数据线上的采样点)。
在这里插入图片描述
  ●提取数据(数据采样):紫色信号处(采样点)MOSI上的数据即为从机接收到的数据,仔细观察采样点几乎在稳定数据的中间点(因为之前所说的边沿检测存在延迟,所以采样点略微偏移中心点,参考链接STM32外部中断边沿触发存在延时问题)。
在这里插入图片描述
✯从机接收数据结果:0xA5
在这里插入图片描述

如有兴趣可查看类似的
IIC协议详解

★★★如有错误欢迎指导。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/181500.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • ue4材质节点介绍_C4D节点材质

    ue4材质节点介绍_C4D节点材质一.基础常量1.Constant快捷键:1+鼠标左键2.Constant2Vector快捷键:2+鼠标左键3.Constant3Vector快捷键:3+鼠标左键4.Constant4Vector快捷键:4+鼠标左键5.StaticBool6.TextureSample7.TestureObject8.ParticleSu…

    2022年10月5日
    9
  • 【转载】一张“神图”看懂单机/集群/热备/磁盘阵列(RAID)

    【转载】一张“神图”看懂单机/集群/热备/磁盘阵列(RAID)

    2021年11月20日
    45
  • 格雷码的实现[通俗易懂]

    格雷码的实现[通俗易懂]问题:产生n位元的所有格雷码。格雷码(GrayCode)是一个数列集合,每个数使用二进位来表示,假设使用n位元来表示每个数字,任两个数之间只有一个位元值不同。例如以下为3位元的格雷码: 000001011010110111101100。如果要产生n位元的格雷码,那么格雷码的个数为2^n.假设原始的值从0开始,格雷码产生的规律是:第一步,改变最右边的

    2025年6月3日
    3
  • HTML的dpr

    HTML的dpr只是为了自己以后查找方便 看不懂勿怪 DPI PPI 屏幕像素密度或屏幕每英寸的像素数量 dips 手机尺寸或独立像素物理像素 手机分辨率 dpr nbsp window devicePixelR js 可以拿到 css 定义 psd 转换 rem 单位 rem px 基准值基准值 手机宽高 dpr 10 乘以 dpr 是因为页面有可能缩放除以 10 是为了取整

    2025年9月22日
    5
  • SpringBoot+Mybatis实现分页查询[通俗易懂]

    SpringBoot+Mybatis实现分页查询[通俗易懂]文章目录前言1.引入依赖2.Mapper中接口3.修改XML文件4.controller层调用接口5.测试总结前言分页查询是在web开发中常用的一种技术,当某个页面查询返回的数据量较大时,为了提高性能和用户体验不能将所有数据一次性返回给过前端,这时候就需要用到分页查询了PageHelper是一款开源的Mybatis第三方物理分页插件,springboot项目中集成PageHelper插件非…

    2022年5月5日
    46
  • IDEA的优化配置

    IDEA的优化配置前言IDEA全称IntelliJIDEA,是java编程语言开发的集成环境。IntelliJ在业界被公认为最好的java开发工具,尤其在智能代码助手、代码自动提示、重构、JavaEE支持、各类版本工具(git、svn等)、JUnit、CVS整合、代码分析、创新的GUI设计等方面的功能可以说是超常的。idea的优化可以使我们更得心应手的高效开发设置优化方法分割线一个文件可能会有一个或多个方法,堆积在一起使人眼花缭乱。方法分割线可以是我们快速区分方法。File——Setting——Edi

    2022年5月21日
    50

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号