一文搞懂——软件模拟SPI

2023-05-16

关于stm32通信协议:软件模拟SPI、软件模拟I2C的总结(fishing_8)_To_be_a_fisher的博客-CSDN博客_stm32 软件spi

发现一篇写的软件模拟SPI的比较容易理解的博客!感兴趣的可以看看

SPI协议

SPI协议,多用于ADC、DA、LCD等设备与MCU之间,要求通信速率要求较高的场合,它相比于I2C来说速度快的多。一般只需要4根线,分别是MISO、MOSI、SCK、CS线等,但是,有可能只用3根。对于SPI通信协议的详细描述我之前有博客已经有说明了,有需要可以了解一下。这里重点是使用软件模拟SPI协议,实验基于野火开发板指南者F103VET6、Flash芯片W25Q64、这里模拟的时序是SPI的模式3(CPOL=1,CPHA=1),原因有两点原因:1、模式3的SCK空闲电平为高,有高电平向低电平翻转较为容易和快,2、模式3在偶数边沿采样,防止第一个信号没采到。

首先,对于软件模拟SPI的GPIO初始化,可以参考SPI的GPIO初始化的配置,我们不用使用复用功能就行,使用普通推挽输出和浮空输入。

void SPI_GPIO_Config(void)
{
	GPIO_InitTypeDef GPIO_InitStruct;
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOC,ENABLE);
	/*SPI CS GPIO Confgi*/
	GPIO_InitStruct.GPIO_Mode	=	GPIO_Mode_Out_PP;
	GPIO_InitStruct.GPIO_Pin	=	EEPROM_SPI_CS_PIN;
	GPIO_InitStruct.GPIO_Speed	=	GPIO_Speed_50MHz;
	GPIO_Init(EEPROM_SPI_CS_PORT,&GPIO_InitStruct);
	
	/*SPI SCK GPIO Config*/
	GPIO_InitStruct.GPIO_Mode	=	GPIO_Mode_Out_PP;
	GPIO_InitStruct.GPIO_Pin	=	EEPROM_SPI_CLK_PIN;
	GPIO_InitStruct.GPIO_Speed	=	GPIO_Speed_50MHz;
	GPIO_Init(EEPROM_SPI_CLK_PORT,&GPIO_InitStruct);
	
	/*MISO GPIO Config*/
	GPIO_InitStruct.GPIO_Mode	=	GPIO_Mode_IN_FLOATING;
	GPIO_InitStruct.GPIO_Pin	=	EEPROM_SPI_MISO_PIN;
	GPIO_Init(EEPROM_SPI_MISO_PORT,&GPIO_InitStruct);
	
	/*MOSI GPIO Config*/
  GPIO_InitStruct.GPIO_Mode	=	GPIO_Mode_Out_PP;
	GPIO_InitStruct.GPIO_Pin	=	EEPROM_SPI_MOSI_PIN;
	GPIO_InitStruct.GPIO_Speed	=	GPIO_Speed_50MHz;
	GPIO_Init(EEPROM_SPI_MOSI_PORT,&GPIO_InitStruct);

	EEPROM_SPI_CS_HIGH();
	EEPROM_SPI_CLK_HIGH();
}

接下来就是最为关键的发送和接收数据函数了,其中注意,使用SPI时,发送和接收其实是同一个函数,通过判断RXNE来确认发送结束,此时也接收完数据,接收数据同样要主机产生时序,时序通过主机发送数据产生,所以会发送无用的Dummy数据。软件模拟不需要,直接接收自己本身会产生时序,不用发送DUMMY数据。Delay函数随便设置,不要小于手册的时间即可。 

软件模拟发送数据

void SPI_SendData(uint8_t data)
{
	uint8_t cnt;
	
	for(cnt=0;cnt<8;cnt++)
	{
		EEPROM_SPI_CLK_LOW();//拉低CLK
		SPI_Delay(10);//这个延时时间任意,但要大于芯片数据手册上的(纳秒级的)
		if(data &0x80)
		{
			EEPROM_SPI_MOSI_HIGH();
		}
		else
		{
			EEPROM_SPI_MOSI_LOW();
		}
		data <<= 1;
		//SPI_Delay(10);
		EEPROM_SPI_CLK_HIGH();//拉高CLK
		SPI_Delay(10);
	}

}

 延时函数

static void SPI_Delay(__IO uint32_t count)
{
	uint32_t i;
	for(i=0;i<count;i++)
	{
		uint8_t uc =12;
		while(uc--);
	}

}

软件模拟接收数据

uint8_t SPI_ReadData(void)
{
	uint8_t i = 0;
	uint8_t value=0;
	for(i=0;i<8;i++)
	{
		EEPROM_SPI_CLK_LOW();
		SPI_Delay(10);
		value<<=1;
		if(EEPROM_SPI_MISO())
		{
			value |= 0x01;
		}
		EEPROM_SPI_CLK_HIGH();
		SPI_Delay(10);
	}
	return value;
}

等待擦除完成

void SPI_WaitErase(void)
{
	uint8_t status = 0x01;
	EEPROM_SPI_CS_LOW();
	SPI_SendData(0x05);
	do
	{
		status = SPI_ReadData();
	}
	while((status&0x01) == 1);
	EEPROM_SPI_CS_HIGH();
}

擦除扇区(Flash存储器的一个特征就是写入之前需要擦除,只能把1改为0,不能把0改写为1)

void SPI_EraseSector(uint32_t addr)
{
		EEPROM_SPI_CS_LOW();
		SPI_SendData(0x06);
		EEPROM_SPI_CS_HIGH();
		EEPROM_SPI_CS_LOW();
		SPI_SendData(0x20);
		SPI_SendData((uint8_t)(addr&0xFF0000)>>16);
		SPI_SendData((addr&0xFF00)>>8);
		SPI_SendData((addr&0xFF));
		EEPROM_SPI_CS_HIGH();
		SPI_WaitErase();
}

检验设备,和前面的程序是否正常,正常才继续后面的页写入和读取数据 ID=0XEF4017

uint32_t SPI_CheckDevice(void)
{
	uint8_t temp0,temp1,temp2;
	uint32_t Device_ID=0;
	EEPROM_SPI_CS_LOW();
	SPI_SendData(0x9F);
	temp0 = SPI_ReadData();
	temp1 = SPI_ReadData();
	temp2 = SPI_ReadData(); 
	EEPROM_SPI_CS_HIGH();
	Device_ID = (temp0<<16) | (temp1<<8) | (temp2);
	return Device_ID;

}

读取数据

void SPI_BufferRead(uint8_t *pBuffer,uint32_t addr,uint32_t numToRead)
{
	EEPROM_SPI_CS_LOW();
	SPI_SendData(0x03);
	SPI_SendData((uint8_t)(addr&0xFF0000)>>16);
	SPI_SendData((addr&0xFF00)>>8);
	SPI_SendData((addr&0xFF));
	while(numToRead--)
	{
		*pBuffer = SPI_ReadData();
		pBuffer++;
	}
	
	EEPROM_SPI_CS_HIGH();

}

页写入

void SPI_PageWrite(uint8_t *pBuffer,uint32_t addr,uint32_t numToWrite)
{
	EEPROM_SPI_CS_LOW();
	SPI_SendData(0x06);
	EEPROM_SPI_CS_HIGH();
	EEPROM_SPI_CS_LOW();
	SPI_SendData(0x02);
	SPI_SendData((addr&0xFF0000)>>16);
	SPI_SendData((addr&0xFF00)>>8);
	SPI_SendData((addr&0xFF));
	
	if(numToWrite>256)
	{
			printf("\r\n页写入最大为256字节\r\n");
			numToWrite = 256 ;
	}
	while(numToWrite--)
	{
		
		SPI_SendData(*pBuffer);
		pBuffer++;
	}
	EEPROM_SPI_CS_HIGH();
	
	SPI_WaitErase();

}


任意大小写入

void SPI_BufferWrite(uint8_t *pBuffer,uint32_t addr,uint32_t numToWrite)
{
	uint32_t count=0,numPage=0,numSingle=0,Addr=0;
	
	Addr = addr%Page_Size;
	count = Page_Size - Addr;
	numPage = numToWrite/Page_Size;
	numSingle = numToWrite%Page_Size;
	
	if(Addr == 0)
	{
		if(numPage==0)
		{
			SPI_PageWrite(pBuffer,addr,numToWrite);
			
		}
		else
		{
			while(numPage--)
			{
				SPI_PageWrite(pBuffer,addr,Page_Size);
				pBuffer+=Page_Size;
				addr+=Page_Size;
			}
			SPI_PageWrite(pBuffer,addr,numSingle);
			
		
		}
	}
	
	else
	{
		if(numPage==0)
		{
			if(numSingle>count)
			{
				SPI_PageWrite(pBuffer,addr,count);
				pBuffer+=count;
				addr+=count;
				SPI_PageWrite(pBuffer,addr,(numSingle-count));
			}
			else
			{
				SPI_PageWrite(pBuffer,addr,numToWrite);

			}
		}
		else
		{
			numToWrite -= count;
			numPage = numToWrite/Page_Size;
			numSingle	=	numToWrite%Page_Size;
			SPI_PageWrite(pBuffer,addr,count);
			addr+=count;
			pBuffer+=count;
			while(numPage--)
			{
				SPI_PageWrite(pBuffer,addr,Page_Size);
				pBuffer+=Page_Size;
				addr+=Page_Size;
				
			}
			if(numSingle!=0)
			{
				SPI_PageWrite(pBuffer,addr,numSingle);
				
			}
			
		
		}
		
	}
	SPI_WaitErase();
}

主函数和读写测试

#include "stm32f10x.h"
#include "bsp_usart.h"
#include "bsp_soft_spi.h"

uint8_t Write_pBuffer[]={"使用野火STM32开发板的软件模拟SPI总算成功了!"};
uint8_t Read_pBuffer[30]={0};
uint8_t pBuffer_Cmp(uint8_t *buffer1,uint8_t *buffer2,uint32_t num)
{
		while(num--)
		{
			if(*buffer1!=*buffer2)
			{
				return 0;
			}
			buffer1++;
			buffer2++;
		}
		return 1;
	
}

void SPI_Test(void)
{
	uint32_t Device_ID = 0;
	Device_ID = SPI_CheckDevice();
	printf("ID=0x%x\r\n",Device_ID);
	if(Device_ID == 0xEF4017)
	{
		printf("SPI FLASH设备正常!\r\n");
		SPI_EraseSector(0x001000);
		SPI_BufferWrite(Write_pBuffer,0x001000,(sizeof(Write_pBuffer)/sizeof(*(Write_pBuffer))-1));
		SPI_BufferRead(Read_pBuffer,0x001000,(sizeof(Write_pBuffer)/sizeof(*(Write_pBuffer))-1));
		if(pBuffer_Cmp(Write_pBuffer,Read_pBuffer,(sizeof(Write_pBuffer)/sizeof(*(Write_pBuffer))-1)) == 1)
		{	
			
			printf("软件模拟SPI的读写数据一致!\r\n");
			printf("\r\n 读出的数据为:%s \r\n", Read_pBuffer);
			
		}
		else
		{
			printf("软件模拟SPI的读写数据不一致!\r\n");
		}
		
	}
	else
	{
		printf("SPI FLASH设备异常!\r\n");
	}
}

int main(void)
{
	
	USART_Config();
	SPI_GPIO_Config();
  /* Output a message on Hyperterminal using printf function */
  printf("\n\r软件模拟SPI实验测试!\n\r");
	SPI_Test();
  while (1)
  {
  }
}




本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

一文搞懂——软件模拟SPI 的相关文章

  • 调试笔记2:SPI+DMA

    一 内容简介 说明 xff1a 关于DMA xff0c SPI的基本知识这里不做介绍 本文只讲述SPI 43 DMA的实现 这里仅实现从外设到内存 从内存到外设也可以参考修改 目的 xff1a 使用STM32作为SPI从机接收数据 xff0
  • pixhawk px4 spi设备驱动

    此篇blog是以nuttx官网介绍为出发点 xff0c 先分析如何初始化的 xff0c 再分析如何读取传感器数据的 xff0c 最后对比了字符型设备操作和spi驱动的实现方式的差别 如有错误还请指正 6 字符型设备 所有的结构体和API都在
  • ((硬件spi+dma)+模拟spi)驱动LCD5110

    span class hljs preprocessor ifndef spi dma h span span class hljs preprocessor define spi dma h span span class hljs pr
  • spi,iic,uart,pcie区别

    一 spi SPI 是英语Serial Peripheral interface的缩写 xff0c 顾名思义就是串行外围设备接口 xff0c 是同步传输协议 xff0c 特征是 xff1a 设备有主机 xff08 master xff09
  • 总线协议一(UART/RS232/RS485/IIC/SPI)

    目录 基础概述 xff1a 一 UART xff08 为串口通信方式 xff09 二 RS232协议 三 RS485协议 四 I2C总线协议 五 SPI总线 六 I2C和SPI的区别 基础概述 xff1a 总线的本质就是一根导线 xff0c
  • 基于I2C/SPI的温湿度采集与OLED显示

    基于I2C SPI的温湿度采集与OLED显示 一 AHT20温湿度采集1 I2C2 温湿度采集代码效果 二 OLED显示1 显示学号姓名2 诗句显示 三 总结四 参考 一 AHT20温湿度采集 1 I2C 解释什么是 软件I2C 和 硬件I
  • SPI接口原理与配置

    SPI接口简介 SPI是英语Serial Peripheral interface的缩写 顾名思义就是串行外围设备接口 是Motorola首先在其MC68HCXX系列处理器上定义的 SPI是一种高速的 全双工 同步的通信总线 并且在芯片的管
  • spi通信

    特点 常规四线通信方式 一根片选线 xff0c 一根时钟线 xff0c 两根数据线 xff1a 一根是主进从出线 xff0c 一根主出从入线 同步 xff0c 全双工 xff0c 通信方式 和谁通信就将谁的片选线拉低 xff0c 每增加一个
  • SPI通讯协议介绍

    来到SPI通讯协议了 废话两句 xff0c SPI很重要 xff0c 这是我在学校时候听那些单片机开发工程师说的 出来实习 xff0c 到后来工作 xff0c 确实如此 xff0c SPI的使用很常见 xff0c 那么自然重要咯 SPI S
  • nRF SPI 与 TWI 操作相关 (BMI088 与 MLX90614 举例)

    SPI0 与 TWI0 的 ID相同 xff0c SPI0 与 TWI0 的 ID相同 编译时有报错 若要避免 xff0c 有两个方法 1 使用不同ID外设 2 使用模拟SPI或模拟IIC nRF SPI 初始化 sdk config h
  • UART,SPI,IIC,RS232通信时序和规则

    一 UART 1 串口通信方式 2 串口通信步骤 注意 xff1a 串口协议规定 xff0c 闲置时必须是高电平 校验位 xff1a 是使用奇偶校验 停止位必须高电平 一个0和多个0区分是靠掐时间 异步通信 xff1a 时钟各不一样 二 I
  • 用STM32F030F4的SPI总线获取BMP280的气压和温度

    1 用STM32Cube MX生成SPI总线的初始化函数 static void BMP280 SPI Init void LL SPI InitTypeDef SPI InitStruct 0 LL GPIO InitTypeDef GP
  • SpringBoot系列--自定义Starter

    提到Spring Boot时 很多人想到的是它的自动化装配特性 当我们项目需要Redis MongoDB时 只需要引入相应的 spring boot starter data redis spring boot starter data m
  • 外设驱动库开发笔记22:ADXL345三轴数字加速度计驱动

    移动设备的广泛应用增加对移动过程中各种参数的检测需求 ADXL345三轴数字加速度计可以用来检测加速度 进而测量倾斜角度等 在这一篇中 我们将讨论ADXL345三轴数字加速度计驱动程序的设计与实现 1 功能概述 ADXL345是一款小而薄的
  • Java SPI机制

    一 SPI机制简介 SPI的全名为Service Provider Interface java spi机制的思想 系统里抽象的各个模块 往往有很多不同的实现方案 在面向的对象的设计里 一般推荐模块之间基于接口编程 模块之间不对实现类进行硬
  • spi,ClassLoader,双亲委托模式

    转载 https www cnblogs com hiyujie p wo xueJava1ClassLoader yu shuang qin wei tuo mo sh html 1 ClassLoader分类 Java虚拟机会创建三类C
  • Android-模块化通信-简单实用的android spi机制

    目录 前言 一 spi是什么 二 ServiceLoader 1 ExportTableLoader 2 ExportTable 3 LazyLoader 4 ServiceLoader 三 应用 四 总结 前言 为了实现Android 模
  • SD卡系列之---SD初始化(SPI)

    SD卡分为SDIO模式与SPI模式 SDIO模式使用SD总线协议 使用4根数据线进行数据传输 SPI使用1收1发2根数据线数据传输 理论上SDIO模式会比SPI模式速度快4倍 但SDIO模式还牵扯到CRC校验位的计算 所以 如果使用CPU有
  • STM32F4 板上的 SPI 从机设置

    我正在尝试通过主从配置中的 SPI 在两个 STM32F4 发现板之间进行通信 我已经有了主设备的代码 但我对需要对从设备的 SPI 初始化进行的更改感到有点困惑 我还想在主机发送数据时实现中断 而不是让从机一直轮询 RXNE 寄存器 但是
  • 无法在 BeagleBone Green Wireless 上配置 SPI0

    注 所选答案为解决该问题的原始答案 更多详细信息请参阅下面的补充答案 我无法在 BeagleBone Green Wireless BBGW 上配置 SPI0 我试图在不使用覆盖层的情况下实现这一目标 仅使用纯 DeviceTree dev

随机推荐

  • 驱动开发基础

    1 Hello驱动 我们应用程序使用open函数的时候 xff0c 会调用内核的sys open函数 xff0c 然后接下来 1 然后打开普通文件的话会使用文件系统操作硬件 xff0c 2 要是打开驱动文件 xff0c 会使用驱动程序对应的
  • ARM架构与编程 · 基于IMX6ULL

    一 嵌入式系统硬件介绍 cpu 43 RAM xff08 内存 xff09 43 FALSH 集成 xff08 flash存储设备 xff09 61 MCU 单片机 AP MPU 进化之后可以外接内存和存储设备 跑复杂的操作系统 xff0c
  • 嵌入式常用算法

    1 冒泡排序 1 两两之间对比 xff0c 要是顺序排 xff0c 一轮过后最大的就是最后一个 2 下一轮参加排序的数比上一轮少一个 include lt iostream gt using namespace std void paixu
  • 实习面试的总结

    2023 4 3 阿凡达机器人 驱动开发实习生 1 怎么注册一个字符设备 注销 1 注册一个设备号 2 设备号加载进内核 3 创建类 4 创建设备 注销 1 从内核中删除 2 删除设备 3 删除类 2 怎么将新加入的网络设备加入到内核中去
  • 字符设备结构体与probe函数

    1 设备结构体 设备结构体 struct ap3216c dev dev t devid 设备号 主设备号 43 次设备号 struct cdev cdev cdev 字符设备对象 xff0c 字符设备驱动的一种结构体类型 struct c
  • SLAM --- VIO 基于 EKF 开源

    1 VIO based on EKF 已知一致性的Visual Inertial EKF SLAM 实现添加链接描述
  • 暗夜精灵7 linux

    Ubuntu18 04 安装nvidia显卡驱动 distro non free 小乌坞的博客 CSDN博客 注意在关闭显示界面的时候需要输入密码 xff0c 不然会一直卡着 在验证是否屏蔽驱动的时候 xff0c 要先重启一下 cuda L
  • linux应用编程

    项目内容 开发板内部使用c语言调用硬件驱动实现各种测试功能 xff0c 保存测试结果 外部程序通过socket接口使用tcp协议与开发板通信进行信息传输 xff0c 最后使用python GUI构造一个界面按照测试顺序逐步显示出各个模块的测
  • NUC10快乐装机

    NUC10装机 由于为了RoboMaster比赛 xff0c 身为全队唯一一个视觉队员兼队长的我 xff0c 经过疫情期间的再三斟酌 xff0c 最后决定工控机选择为nuc10 为什么选择nuc10 作为第一年参赛的新队伍 xff0c 视觉
  • 什么是PID?讲个故事,通俗易懂

    什么是PID xff1f PID xff0c 就是 比例 xff08 proportional xff09 积分 xff08 integral xff09 微分 xff08 derivative xff09 xff0c 是一种很常见的控制算
  • C语言对寄存器的封装

    目录 1 封装总线和外设基地址 2 封装寄存器列表 3 修改寄存器的位操作的方法 把变量的某位清零 把变量的某几个连续位清零 对变量的某几位进行赋值 对变量的某位取反 1 封装总线和外设基地址 在编程上为了方便理解和记忆 xff0c 我们把
  • STM32——串口通信及实验

    目录 1 按照数据传送的方向 xff0c 分为 xff1a 2 按照通信方式 xff0c 分为 xff1a STM32串口通信基础 串口通信过程 UART xff08 USART xff09 框图 串口通信实验 编程要点 代码分析 通信接口
  • 【STM32】DMA原理,配置步骤超详细,一文搞懂DMA

    目录 DMA xff08 Direct Memory Access xff09 简介 DMA传输方式 DMA功能框图 DMA请求映像 DMA1控制器 DMA2控制器 通道 仲裁器 DMA主要特性 DMA处理 DMA数据配置 从哪里来到哪里去
  • [STM32学习]——一文搞懂I2C总线

    目录 I2C总线的概念 I2C最重要的功能包括 xff1a I2C的物理层 I2C主要特点 xff1a I2C的高阻态 I2C物理层总结 xff1a I2C的协议层 初始 xff08 空闲 xff09 状态 开始信号 xff1a 停止信号
  • STM32——ADC采集

    目录 ADC简介 ADC主要特征 ADC功能框图 ADC引脚 电压输入范围 通道选择 单次转换模式 连续转换模式 转换顺序 规则序列 注入序列 触发源 转换时间 中断 转换结束中断 模拟看门狗中断 DMA请求 代码讲解 宏定义 xff1a
  • STM32——MPU6050内部DMP固件移植,获取欧拉角

    MPU6050模块是什么东西 xff0c 我这里就不再赘述了 xff0c 围绕它可以做很多应用 xff0c 比如四翼飞行器 平衡车等等 当然要完全使用这块模块不是那么容易的 解释说明 其实我们主要是想通过MPU6050得到欧拉角和四元数 x
  • 树莓派---设置WIFI自动连接或者取消自动连接

    树莓派 设置WIFI自动连接或者取消自动连接 注意一 方案二 设置WIFI自动连接 2 1 远程连接 若树莓派本地操作则跳过 2 2 修改WIFI自动连接配置文件 三 取消WIFI自动连接 注意 系统 xff1a Ubuntu16 04 树
  • ROS——服务通信

    服务通信是ROS中一种及其常用的通信模式 xff0c 服务通信是基于请求响应模式的 xff0c 是一种应答机制 xff0c 也即一个节点A向另一个节点B发送请求 xff0c B接收处理请求并产生响应返回给A xff0c 比如如下场景 xff
  • ROS中的头文件和源文件

    目录 自定义头文件的调用 头文件 可执行文件 配置文件 自定义源文件的调用 头文件 源文件 可执行文件 配置文件 头文件与源文件相关配置 可执行文件配置 本文主要介绍ROS的C 43 43 实现 xff0c 如何使用头文件和源文件的方式封装
  • 一文搞懂——软件模拟SPI

    关于stm32通信协议 xff1a 软件模拟SPI 软件模拟I2C的总结 xff08 fishing 8 xff09 To be a fisher的博客 CSDN博客 stm32 软件spi 发现一篇写的软件模拟SPI的比较容易理解的博客