SPI总线(W25Qxx书签)

Cortex-M4-SPI总线

SPI总线概述

SPI总线介绍

  SPI是串行外围设备接口(通信协议)、高速、全双工串行的通信总线、占用了3/4根线、有时钟线与数据的输入输出线、有主从结构的。SPI 主要特点有:可以同时发出和接收串行数据;可以当作主机或从机工作;提供频率可编程时钟;发送结束中断标志;写冲突保护;总线竞争保护等。
  SPI 是一个环形总线结构,由 ss(cs)(片选)、sck、sdi(主机输入)、sdo(主机输出) 构成,其时序其实很简单,主要是在 sck 的控制下,两个双向移位寄存器进行数据交换。
三线: CS MOSI/MISO SCK 半双工
四线: CS MOSI MISO SCK 全双工
CS: 片选 ,设备被选,拉低有效的,连接从机
MOSI: 主机输出 从机输入
MISO: 主机输入 从机输出
SCK: 时钟线

选择从机进行通信:
IIC→通过从机的器件地址
SPI→通过片选线选中需要通信的从机
SPI通信原理
  上升沿发送、下降沿接收、高位先发送。
  上升沿到来的时候,sdo 上的电平将被发送到从设备的寄存器中。
  下降沿到来的时候,sdi 上的电平将被接收到主设备的寄存器中。
   假设主机和从机初始化就绪:并且主机的 sbuff=0xaa (10101010),从机的sbuff=0x55 (01010101),下面将分步对 spi 的 8 个时钟周期的数据情况演示一遍(假设上升沿发送数据)。
SPI总线(W25Qxx书签)_第1张图片
   由脉冲0到脉冲1,产生上升沿时,主机发送最高位1,从机发送最高位0,主机sbuff左移一位等待接收数据,从机左移一位接收主机发送的1,等待下降沿时,主机接收数据。持续产生脉冲,实现两个寄存器8位的交换。

SPI总线接口与物理拓扑结构

SPI总线(W25Qxx书签)_第2张图片

SPI总线数据格式

  SPI 总线有四种工作方式(SP0, SP1, SP2, SP3),其中使用的最为广泛的是 SPI0 和 SPI3 方式。
时钟极性(CPOL): CPOL=0 默认电平为低 CPOL=1 默认电平为高
时钟相位(CPHA): CPHA=0 第一个边沿进行采样 CPHA=1 第二个边沿进行采样

模式 时钟极性(CPOL) 时钟相位(SPHA) 工作模式
SP0 0 0 上升沿被采样,下降沿发送数据
SP1 0 1 下降沿被采样,上升沿发送数据
SP2 1 0 下降沿被采样,上升沿发送数据
SP3 1 1 上升沿被采样,下降沿发送数据

SPI总线(W25Qxx书签)_第3张图片

STM32的SPI控制器特征

● 基于三条线的全双工同步传输
● 基于双线的单工同步传输,其中一条可作为双向数据线
● 8 位或 16 位传输帧格式选择
● 主模式或从模式操作
● 多主模式功能
● 8 个主模式波特率预分频器(最大值为 fPCLK/2)
● 从模式频率(最大值为 fPCLK/2)
● 对于主模式和从模式都可实现更快的通信
● 对于主模式和从模式都可通过硬件或软件进行 NSS 管理:动态切换主/从操作
● 可编程的时钟极性和相位
● 可编程的数据顺序,最先移位 MSB 或 LSB
● 可触发中断的专用发送和接收标志

STM32的SPI控制器框架分析(配合寄存器分析)

SPI总线(W25Qxx书签)_第4张图片
MOSI: 数据输出引脚、类似TX
数据方向: TDR----》发送缓冲区—》移位寄存器—》MOSI
MISO: 数据输入引脚、类似RX
数据方向: MISO—》移位寄存器—》接收缓冲区—》RDR
TDR与RDR为同一个寄存器(DR)

/*
函 数 名:Flash_SPI_Config
函数功能:Flash_SPI初始化
返 回 值:无
形    参:无
备    注:
FLASH_SPI1_MOSI -- PA7	-- 复用推挽输出
FLASH_SPI1_MISO -- PA6	-- 复用输入
FLASH_SPI1_CS 	-- PA4	-- 通用推挽输出
FLASH_SPI1_SCLK -- PA5	-- 复用推挽输出
*/
void Flash_SPI_Config(void)
{
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA,ENABLE);					//打开PA时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1,ENABLE);						//打开SPI1时钟
	
	GPIO_InitTypeDef gpio_InitTypeDef = {0};								//定义GPIO结构体
	
	gpio_InitTypeDef.GPIO_Mode 					= GPIO_Mode_AF;				//复用功能
	gpio_InitTypeDef.GPIO_OType			 		= GPIO_OType_PP;			//推挽输出
	gpio_InitTypeDef.GPIO_Pin 					= GPIO_Pin_7;				//PA7
	gpio_InitTypeDef.GPIO_Speed 				= GPIO_Fast_Speed;		//高速
	GPIO_PinAFConfig(GPIOA,GPIO_PinSource7,GPIO_AF_SPI1);					//串口复用功能
	GPIO_Init(GPIOA,&gpio_InitTypeDef);
	
	gpio_InitTypeDef.GPIO_Mode 					= GPIO_Mode_AF;				//复用输入功能
	gpio_InitTypeDef.GPIO_Pin 					= GPIO_Pin_6;				//PA6
	gpio_InitTypeDef.GPIO_Speed 				= GPIO_Fast_Speed;			//高速		
	GPIO_PinAFConfig(GPIOA,GPIO_PinSource6,GPIO_AF_SPI1);					//串口复用功能
	GPIO_Init(GPIOA,&gpio_InitTypeDef);
	
	gpio_InitTypeDef.GPIO_Mode 					= GPIO_Mode_OUT;			//通用输出功能
	gpio_InitTypeDef.GPIO_OType			 		= GPIO_OType_PP;			//推挽输出
	gpio_InitTypeDef.GPIO_Pin 					= GPIO_Pin_4;				//PA4
	gpio_InitTypeDef.GPIO_Speed 				= GPIO_Fast_Speed;			//高速
	GPIO_Init(GPIOA,&gpio_InitTypeDef);
	
	gpio_InitTypeDef.GPIO_Mode 					= GPIO_Mode_AF;				//通用输出功能
	gpio_InitTypeDef.GPIO_OType			 		= GPIO_OType_PP;			//推挽输出
	gpio_InitTypeDef.GPIO_Pin 					= GPIO_Pin_5;				//PA5
	gpio_InitTypeDef.GPIO_Speed 				= GPIO_Fast_Speed;		//高速
	GPIO_PinAFConfig(GPIOA,GPIO_PinSource5,GPIO_AF_SPI1);					//串口复用功能
	GPIO_Init(GPIOA,&gpio_InitTypeDef);
	
	
	SPI1->CR1 &= ~(3<<0);						//时钟相位,时钟极性
	SPI1->CR1 |=  (1<<2);						//主模式选择(注配置)
	SPI1->CR1 &= ~(1<<7);						//先发送MSB
	SPI1->CR1 |= 	(3<<8);						//使能软件从器件管理(片选)
	SPI1->CR1 &= ~(1<<11);						//8位数据帧格式
	SPI1->CR1 &= ~(1<<15);						//双线单项通信数据模式
	SPI1->CR1 |= 	(1<<6);						//SPI使能
		
	
}

/*
函 数 名:SPI1_ExchangeByte
函数功能:数据收发
返 回 值:待发送的数据
形    参:接收到的数据
备    注:
*/
u8 SPI1_ExchangeByte(u8 sData)
{
	//等待发送缓冲区为空
	while(!(SPI1->SR & (1 << 1)));
	SPI1->DR = sData;
	//等待接收缓冲区非空
	while(!(SPI1->SR & (1 << 0)));
	return SPI1->DR;
}

SPI实验(W25Qxx书签)

基本介绍

  W25Q64 (64M-bit),W25Q16(16M-bit)和 W25Q32(32M-bit)是为系统提供一个最小的空间、引脚和功耗的存储器解决方案的串行 Flash 存储器。25Q 系列比普通的串行 Flash 存储器更灵活,性能更优越。基于双倍/四倍的 SPI,它们能够可以立即完成提供数据给 RAM,包括存储声音、文本和数据。芯片支持的工作电压 2.7V 到 3.6V,正常工作时电流小于 5mA,掉电时低于 1uA。所有芯片提供标准的封装。
  W25Q64/16/32 由每页 256 字节组成。每页的 256 字节用一次页编程指令即可完成。每次可以擦除 16 页(1 个扇区)、128 页(32KB 块)、256 页(64KB 块)和全片擦除。
  Flash存储:掉电不丢失、常见的flash设备(U盘 硬件 光盘……)
片选端(CS):
  SPI 片选(/CS)引脚使能和禁止芯片操作。当CS为高电平时,芯片未被选择,串行数据输出(DO、IO0、IO1、IO2 和 IO3)引脚为高阻态。未被选择时,芯片处于待机状态下的低功耗,除非芯片内部在擦除、编程。当/CS 变成低电平,芯片功耗将增长到正常工作,能够从芯片读写数据。上电后,在接收新的指令前,/CS 必须由高变为低电平。上电后,/CS 必须上升到 VCC。在/CS 接上拉电阻可以完成这个。

状态寄存器

SPI总线(W25Qxx书签)_第5张图片

指令表

SPI总线(W25Qxx书签)_第6张图片

各功能实现

写使能

/*
函 数 名:W25Qxx_Write_Enable
函数功能:写使能
返 回 值:无
形    参:无
备    注:
指令码--0x06
*/
void W25Qxx_Write_Enable(void)
{
	SPI_CS = 0;
	SPI1_ExchangeByte(0x06);
	SPI_CS = 1;
}

读状态寄存器
SPI总线(W25Qxx书签)_第7张图片
注: 除了指令外,在芯片内部存储的数据一般默认为0xff。
  读取状态寄存器的指令是 8 位的指令。发送指令之前,先将/ CS 拉低,再发送指令码“05 h”或者“35h”。设备收到读取状态寄存器的指令后,将状态信息(高位)依次移位发送出去,上图所示。读出的状态信息,最低位为 1 代表忙,最低位为 0 代表可以操作,状态信息读取完毕,将片选线拉高。
  读状态寄存器指令可以使用在任何时候,即使程序在擦除的过程中或者写状态寄存器周期正在进中。这可以检测忙碌状态来确定周期是否完成,以确定设备是否可以接受另一个指令

/*
函 数 名:W25Qxx_Read_State
函数功能:状态读取寄存器1
返 回 值:状态寄存器值
形    参:无
备    注:
状态寄存器1 -- 0x05
状态寄存器2 -- 0x35
*/
u16 W25Qxx_Read_State(void)
{
	u16 state;
	SPI_CS = 0;
	SPI1_ExchangeByte(0x05);
	state = SPI1_ExchangeByte(0xff);
	SPI_CS = 1;
	return state;
}

读数据
SPI总线(W25Qxx书签)_第8张图片
  读取数据指令允许按顺序读取一个字节的内存数据。当片选 CS/拉低之后,紧随其后是一个 24 位的地址(A23-A0)(需要发送 3 次,每次 8 个字节,先发高位)。芯片收到地址后,将要读的数据按字节大小转移出去,数据是先转移高位,对于单片机,时钟下降沿发送数据,上升沿接收数据。读数据时,地址会自动增加,允许连续的读取数据。这意味着读取整个内存的数据,只要用一个指令就可以读完。数据读取完成之后,片选信号/ CS 拉高。
  读取数据的指令序列,如上图所示。如果一个读数据指令而发出的时候,设备正在擦除扇区,或者(忙= 1),该读指令将被忽略,也不会对当前周期有什么影响。

/*
函 数 名:W25Qxx_Read_Data
函数功能:读数据
返 回 值:无
形    参:u32 addr,u32 len,u8 *rev_buff
备    注:
*/
void W25Qxx_Read_Data(u32 addr,u16 len,u8 *rev_buff)
{
	SPI_CS = 0;
	SPI1_ExchangeByte(0x03);
	SPI1_ExchangeByte(addr>>16);
	SPI1_ExchangeByte(addr>>8);
	SPI1_ExchangeByte(addr);
	while(len--)
	{
		*rev_buff++=SPI1_ExchangeByte(0xff);
	}
	SPI_CS = 1;
	while((W25Qxx_Read_State()&(1<<0)));
}

页编程
SPI总线(W25Qxx书签)_第9张图片
  页编程指令允许从一个字节到 256 字节的数据编程(一页)(编程之前必须保证内存空间是 0XFF)。允许写入指令之前,必须先发送设备写使能指令。写使能开启后,设备才能接收编程指令。开启页编程先拉底/ CS,然后发送指令代码“02 h”,接着发送一个 24 位地址(A23-A0)(发送 3 次,每次 8 位) 和至少一个数据字节(数据字节不能超过 256字节)。数据字节发送完毕,需要拉高片选线 CS/,,并判断状态位,等待写入结束。
  进行页编程时,如果数据字节数超过了 256 字节,地址将自动回到页的起始地址,覆盖掉之前的数据。在某些情况下,数据字节小于 256 字节(同一页内),也可以正常对其他字节存放,不会有任何影响。如果存放超过 256 字节的数据,需要分次编程存放。

/*
函 数 名:W25Qxx_Page_Program
函数功能:页编程
返 回 值:无
形    参:u32 addr,u32 len,u8 *send_buff
备    注:
写之前擦除一下,避免数据紊乱
*/
void W25Qxx_Page_Program(u32 addr,u16 len,u8 *send_buff)
{
	W25Qxx_Write_Enable();
	SPI_CS = 0;
	SPI1_ExchangeByte(0x02);
	SPI1_ExchangeByte(addr>>16);
	SPI1_ExchangeByte(addr>>8);
	SPI1_ExchangeByte(addr);
	while(len--)
	{
		SPI1_ExchangeByte(*send_buff++);
	}
	SPI_CS = 1;
	while((W25Qxx_Read_State()&(1<<0)));
}
/*

扇区擦除
SPI总线(W25Qxx书签)_第10张图片
  扇区擦除指令可以擦除指定一个扇区(4 k 字节)内所有数据,将内存空间恢复到 0xFF 状态。写入扇区擦除指令之前必须执行设备写使能(发送设备写使能指令 0x06),并判断状态寄存器(状态寄存器位最低位必须等于 0才能操作)。发送的扇区擦除指令前,先拉低/ CS,接着发送扇区擦除指令码”20 h”,和 24 位地址(A23-A0),地址发送完毕后,拉高片选线 CS/,,并判断状态位,等待擦除结束。擦除一个扇区的最少需要 150ms 时间。

/*
函 数 名:W25Qxx_Sector_Erase
函数功能:扇擦除
返 回 值:无
形    参:u32 addr --待删除得扇区首地址
备    注:
*/
void W25Qxx_Sector_Erase(u32 addr)
{
	W25Qxx_Write_Enable();
	while((W25Qxx_Read_State()&(1<<0)));
	SPI_CS = 0;
	SPI1_ExchangeByte(0x20);
	SPI1_ExchangeByte(addr>>16);
	SPI1_ExchangeByte(addr>>8);
	SPI1_ExchangeByte(addr);
	SPI_CS = 1;
	while((W25Qxx_Read_State()&(1<<0)));
}

跨页写

/*
函 数 名:W25Qxx_Stride_Pagewrite
函数功能:跨页写数据
返 回 值:无
形    参:u32 addr,u32 len,u8 *rev_buff
备    注:
使用之前擦除一下
*/
void W25Qxx_Stride_Pagewrite(u32 addr,u16 length,u8 *send_buff)
{
	u16 n = 256-addr%256;
	if(length<=n)
	{
		n = length;	
	}
		do{
			W25Qxx_Page_Program(addr,n,send_buff);
			addr = addr+n;
			send_buff+=n;
			length-=n;
			if(length>256)
			{
				n =256;
			}
			else{
				n = length;
			}
		}while(length);

}

注: 当使用页编程或跨页写时,需注意存放数据的数组大小,在STM32当中,局部变量存储的栈空间只能存储0x4000字节的数据。
SPI总线(W25Qxx书签)_第11张图片

你可能感兴趣的:(Crotex-M4笔记,单片机,stm32,arm)