当前位置:   article > 正文

STM32第十八课:SPI&Flash

STM32第十八课:SPI&Flash


需求

通过SPI控制FLash进行数据的保存和删除。
在这里插入图片描述


一、SPI概要

在我们使用UART(通用串行异步通讯协议)时,因为UART没有时钟信号,速度不同,无法控制何时收发数据
非要解决这个问题的话,需要为UART传输的数据添加起始位停止位,双方波特率还需同步,很麻烦,比较垃圾。
于是由摩托摩拉公司牵头推出了一种新的通讯总线 SPI:串行外设接口
SPI是一个高速、全双工、同步的串行通信总线。应用场景:OLED屏幕、FLASH存储器、AD转换器
通信方式:串行同步全双工(人话就是数据在线上按照时间顺序一位一位的传输,发送和接收时要在通信时钟的同步下进行数据传输,且可以同时发送和接收)
该总线就是利用单独的数据线(MISO和MOSI)和单独的时钟信号线(SCK)完美解决了收发端的数据完美同步。
接线
MOSI: 主设备输出、从设备输入 TX
MISO: 主设备输入、从设备输出 RX
SCK: 时钟线
CS: 片选信号线 一主多从,拉低选择和哪个从机通信
GND: 地线
单从机时:
在这里插入图片描述
多从机时:
在这里插入图片描述
总结:除了SS片选线需要单独备一根线和从机进行一对一的链接,其他都可以一对多,多个从机接同一个主机接口。
SPI通信模式:环形传输,发多少收多少。
操作逻辑:
1、用时先拉低CS。
2、开始操作发数据,收数据。
3、用完后拉高CS。

STM32中SPI接口
在这里插入图片描述

二、SPI配置

1.开时钟

打开原理图,找到引脚
在这里插入图片描述

在这里插入图片描述
根据引脚的开SPI2和GPIOB的时钟

	//1,开时钟,GPIO SPI2 
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2,ENABLE);
  • 1
  • 2
  • 3

2.配置IO

MOSI,SCK:复用推挽输出
均需要输出高低电平,还是复用功能,所以配复用推挽输出
MISO:浮空输入
需要接收数据,没什么好说的
CS:通用推挽输出
由于是主机所以CS当做GPIO配就行(软件模式)
从机的话配硬件模式

	//2,配置IO模式
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
	GPIO_InitStruct.GPIO_Pin =GPIO_Pin_12;//CS
	GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB,&GPIO_InitStruct);
	
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;
	GPIO_InitStruct.GPIO_Pin =GPIO_Pin_13|GPIO_Pin_15;//SCK和MOSI
	GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB,&GPIO_InitStruct);	
	
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;
	GPIO_InitStruct.GPIO_Pin =GPIO_Pin_14;//MISO
	GPIO_Init(GPIOB,&GPIO_InitStruct);	
	
	GPIO_SetBits(GPIOB,GPIO_Pin_12);//拉高CS避免通信出错
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

最后拉高CS是因为当CS拉低时表示开始传输信号

3.配置&使能SPI


参考手册中的步骤如下:

在这里插入图片描述
CPHA:时钟相位, CPHA=0时,在时钟的第一个边沿进行采样,第二个边沿进行输出
              CPHA=1时,在时钟的第二个边沿进行采样,第三个边沿进行输出
CPOL:时钟极性, CPOL=0时,空闲时时钟为低电平
              CPOL=1时,空闲时时钟为高电平

SPI的模式0CPHA=0,CPOL=0 上升沿采样、下降沿接受
SPI的模式1CPHA=0,CPOL=1 下降沿采样、上升沿接受
SPI的模式2CPHA=1,CPOL=0 下降沿采样、上升沿接受
SPI的模式3CPHA=1,CPOL=1 上升沿采样、下降沿接受

  一般能够支持SPI模式0的设备也支持SPI模式3,支持模式1的设备也支持模式2。SPI通信没有具体的协议格式,格式根据通信对象的要求来定。一般的传输数据时候采用的就是8位进行,高位先传还是低位先传,传输的速度。这些参数都需要根据通信对象来进行配置

	//2,配置SPI
	SPI_InitStruct.SPI_BaudRatePrescaler=SPI_BaudRatePrescaler_2;//时钟分频2
	SPI_InitStruct.SPI_CPHA=SPI_CPHA_1Edge;//时钟相位,第一个边沿采样
	SPI_InitStruct.SPI_CPOL=SPI_CPOL_Low;//时钟极性 ,低电平
	SPI_InitStruct.SPI_CRCPolynomial=0x12;//不使用CRC校验,参数无意义
	SPI_InitStruct.SPI_DataSize=SPI_DataSize_8b;//数据宽度8未
	SPI_InitStruct.SPI_Direction=SPI_Direction_2Lines_FullDuplex;//双线全双工模式
	SPI_InitStruct.SPI_FirstBit=SPI_FirstBit_MSB;//先发高位
	SPI_InitStruct.SPI_Mode=SPI_Mode_Master;//主机模式
	SPI_InitStruct.SPI_NSS=SPI_NSS_Soft;//软件NSS模式
	
	SPI_Init(SPI2,&SPI_InitStruct);
	//3,使能SPI
	SPI_Cmd(SPI2,ENABLE);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

本此SPI配置的对象是FLash,需求如下:
在这里插入图片描述

三、FLash操作函数

  本次用到的FLash为W25Q64,是华邦存储推出的一款串行FLASH存储芯片。
空间规格:
64Mbit (兆位)== 8Mbyte(兆字节)
256字节为一页、16页为一扇区、16扇区为一块
在这里插入图片描述

地址表示:
在这里插入图片描述

1.SPI发送数据

由于SPI是环形传输,所以发多少,我们也要接收多少。
在这里插入图片描述
发送:判断发送缓冲器空闲标志(TXE)是否为0,为0表示正在发,此时持续等待数据发完。标志为1表示之前的发送完了,空闲下来了。此时就可以开始往DR发数据。
接收:判断接收缓冲器非空(RXNE)是否为0,为0表示接受缓冲区为空,没数据,此时持续等待数据到来。标志为1表示数据来了,此时就可以开始读DR的数据了。

/*****************************************************************

 *函 数 名 称:SPI2_SendData
 *函 数 功 能:SPI2发送数据
 *函 数 形 参:SPI2_SendData 发送内容
 *函 数 返 回:SPI2_RecvData 返回收到的数据
 *作       者:ZHT
 *修 改 日 期:xx\xx\xx

*******************************************************************/
uint8_t SPI2_SendData(uint8_t SPI2_SendData)
{
	uint8_t SPI2_RecvData = 0;
	while((SPI2->SR & (0x1<<1)) == 0);
	SPI2->DR = SPI2_SendData;
	while((SPI2->SR & (0x1<<0)) == 0);
	SPI2_RecvData = SPI2->DR;
	return SPI2_RecvData;
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

2.FLASH写使能

看手册上的时序:
在这里插入图片描述
得知逻辑:
1.先拉低CS。
2.SPI发出06指令。
3.最后拉高CS。

/*****************************************************************
 *函 数 名 称:Write_Enable
 *函 数 功 能:FLASH写使能操作
 *函 数 形 参:无
 *函 数 返 回:无
 *作       者:ZHT
 *修 改 日 期:xx/xx/xx
*******************************************************************/
void Write_Enable(void)
{
	//拉低CS
	GPIO_ResetBits(GPIOB,GPIO_Pin_12);
	//发送0x06指令
	SPI2_SendData(0x06);
	//拉高CS
	GPIO_SetBits(GPIOB,GPIO_Pin_12);	
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

3.FLASH等待操作完成

看手册上的时序:
在这里插入图片描述

在这里插入图片描述
得知逻辑:
1.先拉低CS。
2.发送05或35指令。
3.循环往FLash发送数据(无所谓什么数据,只是为了置换出FLash状态寄存器的值),判断寄存器的第0位BUSY是否为0,为0时就代表Flash为空闲状态,可以执行其他操作。
4.判断结束就拉高CS。

/*****************************************************************
 *函 数 名 称:Flash_WaitForWriteEnd
 *函 数 功 能:FLASH等待操作完成
 *函 数 形 参:无
 *函 数 返 回:无
 *作       者:ZHT
 *修 改 日 期:xx/xx/xx
*******************************************************************/
void Flash_WaitForWriteEnd(void)
{
	uint8_t recv = 0;
	//拉低CS
	GPIO_ResetBits(GPIOB,GPIO_Pin_12);
	SPI2_SendData(0x05);//发送读取指令
	do{
		recv = SPI2_SendData(0x55);//循环读取寄存器数据,0x55随便改
	}while((recv & 0x01) == SET);//判断寄存器的第0位是否是1
	//拉高CS
	GPIO_SetBits(GPIOB,GPIO_Pin_12);	
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

4.FLASH页写操作

顾名思义,就是往FLash中写入一页(256Byte)的操作。
手册上的时序:
在这里插入图片描述
得出逻辑:
1.由于要进行写操作,所以要先进行写使能。
2.拉低CS,发送02指令。
3.发送三个字节,即24位的地址,每次发8位分3次发送。为了告知写入的位置。
4.发送数据,8位8位发,最多256。
5.调用FLash等待函数,等待写入完成。
6.写入完成后,拉高CS。

/*****************************************************************
 *函 数 名 称:Flash_WritePage
 *函 数 功 能:FLASH页写操作
 *函 数 形 参:Write_Local:写入地址 Write_data:写入数据 Write_len:写入数据长度
 *函 数 返 回:无
 *作       者:ZHT
 *修 改 日 期:xx/xx/xx
*******************************************************************/
void Flash_WritePage(uint8_t *Write_data,uint32_t Write_Local,uint16_t Write_len)
{
	//1,先写使能
	Write_Enable();
	//2,拉低CS
	GPIO_ResetBits(GPIOB,GPIO_Pin_12);
	//3,发送命令
	SPI2_SendData(0x02);
	//4,发送三个字节地址 add = 0x123456
	SPI2_SendData((Write_Local&0xFF0000)>> 16);
	SPI2_SendData((Write_Local&0x00FF00) >> 8);
	SPI2_SendData((Write_Local&0x0000FF));
	while(Write_len--)
	{
		SPI2_SendData( *Write_data);
		Write_data++;
	}
	//5,拉高CS
	GPIO_SetBits(GPIOB,GPIO_Pin_12);
	//6,等待操作完成
	Flash_WaitForWriteEnd();
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30

5.FLASH读操作

手册上的时序:
在这里插入图片描述
得出逻辑:
1.拉低CS,发送03指令。
2.发送3字节读取的地址。
3.随便发送1个字节的数据,返回值就是要获取的数据。
4.拉高CS。(千万不要等待操作完成,因为手册里没写)

/*****************************************************************
 *函 数 名 称:Flash_ReadData
 *函 数 功 能:FLASH读操作
 *函 数 形 参:add:读取地址 data:保存读取数据 len:读取数据长度
 *函 数 返 回:无
 *作       者:ZHT
 *修 改 日 期:xx/xx/xx
*******************************************************************/
void Flash_ReadData(uint32_t add, uint8_t *data,uint16_t len)
{
	//1,拉低CS
	GPIO_ResetBits(GPIOB,GPIO_Pin_12);	
	//2,发送命令
	SPI2_SendData(0x03);	
	//3,发送三个字节地址 add = 0x123456
	SPI2_SendData((add&0xFF0000)>> 16);
	SPI2_SendData((add&0x00FF00) >> 8);
	SPI2_SendData((add&0x0000FF));
	while(len--)
	{
		*data = SPI2_SendData(0x55);
		data++;
	}
	//4,拉高CS
	GPIO_SetBits(GPIOB,GPIO_Pin_12);	
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26

6.FLASH扇区擦除

看时序图
在这里插入图片描述
逻辑:
1.擦除操作实际上也是写入操作,是往Flash中写入0xFF。所以此时也要先开写使能。
2.拉低CS,发送指令20。
3.发送三个字节。
4.拉高电平,等待操作完成。

/*****************************************************************
 *函 数 名 称:Flash_SectorErase
 *函 数 功 能:FLASH扇区擦除
 *函 数 形 参:add:读取地址
 *函 数 返 回:无
 *作       者:CYM
 *修 改 日 期:xx/xx/xx
*******************************************************************/
void Flash_SectorErase(uint32_t add)
{
	//先写使能
	Write_Enable();
	//1,拉低CS
	GPIO_ResetBits(GPIOB,GPIO_Pin_12);	
	//2,发送命令
	SPI2_SendData(0x20);	
	//3,发送三个字节地址 add = 0x123456
	SPI2_SendData(add >> 16);
	SPI2_SendData((add&0x00FF00) >> 8);
	SPI2_SendData((add&0x0000FF));	
	//4,拉高CS
	GPIO_SetBits(GPIOB,GPIO_Pin_12);		
	//5,等待忙标志变0
	Flash_WaitForWriteEnd();
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25

四、需求实现

main.c

#include "stm32f10x.h"
#include "usart.h"
#include "stdio.h"
#include "delay.h"
#include "string.h"
#include "pwm.h"
#include "adc.h"
#include "su03t.h"
#include "dht11.h"
#include "kqm.h"
#include "key.h"
#include "RTC.h"
#include "bsp_lcd.h"
#include "wifi.h"
#include "aliot.h"
#include "time.h"
#include "spi.h"
#define TestAddr 0x000700
char buff1[20]; 
int main()
{
		NVIC_SetPriorityGrouping(5);//两位抢占两位次级
    Usart1_Config(); 
		SysTick_Config(72000);
	  SPI2_Config();
		Flash_ReadID();
		Flash_SectorErase(TestAddr);
	  FLASH_WriteBuffer((u8*)"好饿好饿好饿~",TestAddr,300);
		printf("写数据后\r\n");
		
		Flash_ReadData(TestAddr,buff1,300);
		printf("FLASH:%s\r\n",buff1);
		Flash_SectorErase(TestAddr);
		printf("清数据后\r\n");
		
		Flash_ReadData(TestAddr,buff1,300);
		printf("FLASH:%s\r\n",buff1);
	  FLASH_WriteBuffer((u8*)"好饿好饿好饿~",TestAddr,300);
		printf("重写数据后\r\n");
	
		Flash_ReadData(TestAddr,buff1,300);
		printf("FLASH:%s\r\n",buff1);
		
		 while(1)
    {	
			
			if(ledcnt[0]>=ledcnt[1])//2S一次
			{
				ledcnt[0]=0;
				printf("FLASH:%s\r\n",buff1);
			}
    }
		return 0;
}



  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57

spi.c

#include "spi.h"
#define sFLASH_SPI_PAGESIZE       0x100
/*****************************************************************

 *函 数 名 称:SPI2_Config

 *函 数 功 能:初始化SPI2
 
 *函 数 形 参:无
 *函 数 返 回:无
 *作       者:ZHT
 
 *修 改 日 期:xx\xx\xx

*******************************************************************/
void SPI2_Config()
{
#if 0
	//1,开时钟,GPIO SPI2  代码简短,但不直观
	RCC->APB2ENR |= 1<<3;
	RCC->APB1ENR |= 1<<14;
	//2,配置IO模式
	GPIOB->CRH &= ~(0xFFFF<<16);
	GPIOB->CRH |= 0xB4B3<<16;//配置4个IO口的模式 1011
	GPIOB->ODR |= 0x1<<12;//拉高PB12,避免对通信产生影响
	//3,配置SPI
	SPI2->CR1 |= 0x0244;//配置SPI工作模式
	#else
	GPIO_InitTypeDef GPIO_InitStruct={0};
	SPI_InitTypeDef SPI_InitStruct={0};
	//1,开时钟,GPIO SPI2 
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2,ENABLE);
	//2,配置IO模式
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
	GPIO_InitStruct.GPIO_Pin =GPIO_Pin_12;//CS
	GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB,&GPIO_InitStruct);
	
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;
	GPIO_InitStruct.GPIO_Pin =GPIO_Pin_13|GPIO_Pin_15;//SCK和MOSI
	GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB,&GPIO_InitStruct);	
	
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;
	GPIO_InitStruct.GPIO_Pin =GPIO_Pin_14;//MISO
	GPIO_Init(GPIOB,&GPIO_InitStruct);	
	
	GPIO_SetBits(GPIOB,GPIO_Pin_12);//拉高CS避免通信出错
	//2,配置SPI
	SPI_InitStruct.SPI_BaudRatePrescaler=SPI_BaudRatePrescaler_2;//时钟分频2
	SPI_InitStruct.SPI_CPHA=SPI_CPHA_1Edge;//时钟相位,第一个边沿采样
	SPI_InitStruct.SPI_CPOL=SPI_CPOL_Low;//时钟极性 ,低电平
	SPI_InitStruct.SPI_CRCPolynomial=0x12;//不使用CRC校验,参数无意义
	SPI_InitStruct.SPI_DataSize=SPI_DataSize_8b;//数据宽度8未
	SPI_InitStruct.SPI_Direction=SPI_Direction_2Lines_FullDuplex;//双线全双工模式
	SPI_InitStruct.SPI_FirstBit=SPI_FirstBit_MSB;//先发高位
	SPI_InitStruct.SPI_Mode=SPI_Mode_Master;//主机模式
	SPI_InitStruct.SPI_NSS=SPI_NSS_Soft;//软件NSS模式
	
	SPI_Init(SPI2,&SPI_InitStruct);
	//3,使能SPI
	SPI_Cmd(SPI2,ENABLE);
	#endif
}


/*****************************************************************

 *函 数 名 称:SPI2_SendData
 *函 数 功 能:SPI2发送数据
 *函 数 形 参:SPI2_SendData 发送内容
 *函 数 返 回:SPI2_RecvData 返回收到的数据
 *作       者:ZHT
 *修 改 日 期:xx\xx\xx

*******************************************************************/
uint8_t SPI2_SendData(uint8_t SPI2_SendData)
{
	uint8_t SPI2_RecvData = 0;
	while((SPI2->SR & (0x1<<1)) == 0);
	SPI2->DR = SPI2_SendData;
	while((SPI2->SR & (0x1<<0)) == 0);
	SPI2_RecvData = SPI2->DR;
	return SPI2_RecvData;
}

/************************SPI-FLASH********************************/
/*****************************************************************
 *函 数 名 称:Write_Enable
 *函 数 功 能:FLASH写使能操作
 *函 数 形 参:无
 *函 数 返 回:无
 *作       者:ZHT
 *修 改 日 期:xx/xx/xx
*******************************************************************/
void Write_Enable(void)
{
	//拉低CS
	GPIO_ResetBits(GPIOB,GPIO_Pin_12);
	//发送0x06指令
	SPI2_SendData(0x06);
	//拉高CS
	GPIO_SetBits(GPIOB,GPIO_Pin_12);	
}

/*****************************************************************
 *函 数 名 称:Flash_WaitForWriteEnd
 *函 数 功 能:FLASH等待操作完成
 *函 数 形 参:无
 *函 数 返 回:无
 *作       者:CYM
 *修 改 日 期:xx/xx/xx
*******************************************************************/
void Flash_WaitForWriteEnd(void)
{
	uint8_t recv = 0;
	//拉低CS
	GPIO_ResetBits(GPIOB,GPIO_Pin_12);
	SPI2_SendData(0x05);//发送读取指令
	do{
		recv = SPI2_SendData(0x55);//循环读取寄存器数据,0x55随便改
	}while((recv & 0x01) == SET);//判断寄存器的第0位是否是1
	//拉高CS
	GPIO_SetBits(GPIOB,GPIO_Pin_12);	
}

/*****************************************************************
 *函 数 名 称:Flash_WritePage
 *函 数 功 能:FLASH页写操作
 *函 数 形 参:Write_Local:写入地址 Write_data:写入数据 Write_len:写入数据长度
 *函 数 返 回:无
 *作       者:CYM
 *修 改 日 期:xx/xx/xx
*******************************************************************/
void Flash_WritePage(uint8_t *Write_data,uint32_t Write_Local,uint16_t Write_len)
{
	//1,先写使能
	Write_Enable();
	//2,拉低CS
	GPIO_ResetBits(GPIOB,GPIO_Pin_12);
	//3,发送命令
	SPI2_SendData(0x02);
	//4,发送三个字节地址 add = 0x123456
	SPI2_SendData((Write_Local&0xFF0000)>> 16);
	SPI2_SendData((Write_Local&0x00FF00) >> 8);
	SPI2_SendData((Write_Local&0x0000FF));
	while(Write_len--)
	{
		SPI2_SendData( *Write_data);
		Write_data++;
	}
	//5,拉高CS
	GPIO_SetBits(GPIOB,GPIO_Pin_12);
	//6,等待操作完成
	Flash_WaitForWriteEnd();
}

/*****************************************************************
 *函 数 名 称:Flash_ReadData
 *函 数 功 能:FLASH读操作
 *函 数 形 参:add:读取地址 data:保存读取数据 len:读取数据长度
 *函 数 返 回:无
 *作       者:CYM
 *修 改 日 期:xx/xx/xx
*******************************************************************/
void Flash_ReadData(uint32_t add, uint8_t *data,uint16_t len)
{
	//1,拉低CS
	GPIO_ResetBits(GPIOB,GPIO_Pin_12);	
	//2,发送命令
	SPI2_SendData(0x03);	
	//3,发送三个字节地址 add = 0x123456
	SPI2_SendData((add&0xFF0000)>> 16);
	SPI2_SendData((add&0x00FF00) >> 8);
	SPI2_SendData((add&0x0000FF));
	while(len--)
	{
		*data = SPI2_SendData(0x55);
		data++;
	}
	//4,拉高CS
	GPIO_SetBits(GPIOB,GPIO_Pin_12);	
}
/*****************************************************************
 *函 数 名 称:Flash_SectorErase
 *函 数 功 能:FLASH扇区擦除
 *函 数 形 参:add:读取地址
 *函 数 返 回:无
 *作       者:CYM
 *修 改 日 期:xx/xx/xx
*******************************************************************/
void Flash_SectorErase(uint32_t add)
{
	//先写使能
	Write_Enable();
	//1,拉低CS
	GPIO_ResetBits(GPIOB,GPIO_Pin_12);	
	//2,发送命令
	SPI2_SendData(0x20);	
	//3,发送三个字节地址 add = 0x123456
	SPI2_SendData(add >> 16);
	SPI2_SendData((add&0x00FF00) >> 8);
	SPI2_SendData((add&0x0000FF));	
	//4,拉高CS
	GPIO_SetBits(GPIOB,GPIO_Pin_12);		
	//5,等待忙标志变0
	Flash_WaitForWriteEnd();
}
/*****************************************************************
 *函 数 名 称:Flash_ReadID
 *函 数 功 能:FLASH读取ID
 *函 数 形 参:无
 *函 数 返 回:无
 *作       者:ZHT
 *修 改 日 期:xx/xx/xx
*******************************************************************/
void Flash_ReadID(void)
{
 uint32_t Temp = 0, Temp0 = 0, Temp1 = 0, Temp2 = 0;

	//1,拉低CS
	GPIO_ResetBits(GPIOB,GPIO_Pin_12);	
  SPI2_SendData(0x9F);
  Temp0 = SPI2_SendData(0x55);
  Temp1 = SPI2_SendData(0x55);
  Temp2 = SPI2_SendData(0x55);
  GPIO_SetBits(GPIOB,GPIO_Pin_12);	
  Temp = (Temp0 << 16) | (Temp1 << 8) | Temp2;
	printf("FLASH_ID:%x\r\n",Temp);
}

/*****************************************************************

 *函 数 名 称:Flash_ChipClean
 *函 数 功 能:全片擦除
 
 *函 数 形 参:无
 *函 数 返 回:无
 *作       者:ZHT
 
 *修 改 日 期:xx\xx\xx

*******************************************************************/
void Flash_ChipClean()
{
	//先写使能
	Write_Enable();
	//1,拉低CS
	GPIO_ResetBits(GPIOB,GPIO_Pin_12);	
	//2,发送命令
	SPI2_SendData(0x60);	
		//4,拉高CS
	GPIO_SetBits(GPIOB,GPIO_Pin_12);	
		Flash_WaitForWriteEnd();
}


/*****************************************************************

 *函 数 名 称:FLASH_WriteBuffer
 *函 数 功 能:随意写入
 
 *函 数 形 参:uint8_t* pBuffer:指向要写入数据的缓冲区的指针。
							 uint32_t WriteAddr:写入数据的起始地址。
							 uint16_t NumByteToWrite:要写入的字节数。
 *函 数 返 回:无
 *作       者:ZHT
 
 *修 改 日 期:xx\xx\xx

*******************************************************************/

void FLASH_WriteBuffer(uint8_t* pBuffer, uint32_t WriteAddr, uint16_t NumByteToWrite)
{
  uint8_t NumOfPage = 0,NumOfSingle = 0,Addr = 0,count = 0,temp = 0;
//       整页的数量      不足一页的剩余字节数  起始地址偏移		 当前页剩余可写入的字节数	 临时变量,用于存储超出当前页的字节数
  Addr = WriteAddr % 256;
  count = 0x100 - Addr;
  NumOfPage =  NumByteToWrite / 256;
  NumOfSingle = NumByteToWrite % 256;

  if (Addr == 0) /*!< WriteAddr is sFLASH_PAGESIZE aligned  */
  {
    if (NumOfPage == 0) /*!< NumByteToWrite < sFLASH_PAGESIZE */
    {
      Flash_WritePage(pBuffer, WriteAddr, NumByteToWrite);
    }
    else /*!< NumByteToWrite > sFLASH_PAGESIZE */
    {
      while (NumOfPage--)
      {
        Flash_WritePage(pBuffer, WriteAddr, 256);
        WriteAddr +=  256;
        pBuffer += 256;
      }

      Flash_WritePage(pBuffer, WriteAddr, NumOfSingle);
    }
  }
  else /*!< WriteAddr is not sFLASH_PAGESIZE aligned  */
  {
    if (NumOfPage == 0) /*!< NumByteToWrite < sFLASH_PAGESIZE */
    {
      if (NumOfSingle > count) /*!< (NumByteToWrite + WriteAddr) > sFLASH_PAGESIZE */
      {
        temp = NumOfSingle - count;

        Flash_WritePage(pBuffer, WriteAddr, count);
        WriteAddr +=  count;
        pBuffer += count;

        Flash_WritePage(pBuffer, WriteAddr, temp);
      }
      else
      {
        Flash_WritePage(pBuffer, WriteAddr, NumByteToWrite);
      }
    }
    else /*!< NumByteToWrite > sFLASH_PAGESIZE */
    {
      NumByteToWrite -= count;
      NumOfPage =  NumByteToWrite / 256;
      NumOfSingle = NumByteToWrite % 256;

      Flash_WritePage(pBuffer, WriteAddr, count);
      WriteAddr +=  count;
      pBuffer += count;

      while (NumOfPage--)
      {
        Flash_WritePage(pBuffer, WriteAddr, 256);
        WriteAddr +=  256;
        pBuffer += 256;
      }

      if (NumOfSingle != 0)
      {
        Flash_WritePage(pBuffer, WriteAddr, NumOfSingle);
      }
    }
  }
}

void sFLASH_WriteBuffer(uint8_t* pBuffer, uint32_t WriteAddr, uint16_t NumByteToWrite)
{
  uint8_t NumOfPage = 0, NumOfSingle = 0, Addr = 0, count = 0, temp = 0;

  Addr = WriteAddr % sFLASH_SPI_PAGESIZE;
  count = sFLASH_SPI_PAGESIZE - Addr;
  NumOfPage =  NumByteToWrite / sFLASH_SPI_PAGESIZE;
  NumOfSingle = NumByteToWrite % sFLASH_SPI_PAGESIZE;

  if (Addr == 0) /*!< WriteAddr is sFLASH_PAGESIZE aligned  */
  {
    if (NumOfPage == 0) /*!< NumByteToWrite < sFLASH_PAGESIZE */
    {
      Flash_WritePage(pBuffer, WriteAddr, NumByteToWrite);
    }
    else /*!< NumByteToWrite > sFLASH_PAGESIZE */
    {
      while (NumOfPage--)
      {
        Flash_WritePage(pBuffer, WriteAddr, sFLASH_SPI_PAGESIZE);
        WriteAddr +=  sFLASH_SPI_PAGESIZE;
        pBuffer += sFLASH_SPI_PAGESIZE;
      }

      Flash_WritePage(pBuffer, WriteAddr, NumOfSingle);
    }
  }
  else /*!< WriteAddr is not sFLASH_PAGESIZE aligned  */
  {
    if (NumOfPage == 0) /*!< NumByteToWrite < sFLASH_PAGESIZE */
    {
      if (NumOfSingle > count) /*!< (NumByteToWrite + WriteAddr) > sFLASH_PAGESIZE */
      {
        temp = NumOfSingle - count;

        Flash_WritePage(pBuffer, WriteAddr, count);
        WriteAddr +=  count;
        pBuffer += count;

        Flash_WritePage(pBuffer, WriteAddr, temp);
      }
      else
      {
        Flash_WritePage(pBuffer, WriteAddr, NumByteToWrite);
      }
    }
    else /*!< NumByteToWrite > sFLASH_PAGESIZE */
    {
      NumByteToWrite -= count;
      NumOfPage =  NumByteToWrite / sFLASH_SPI_PAGESIZE;
      NumOfSingle = NumByteToWrite % sFLASH_SPI_PAGESIZE;

      Flash_WritePage(pBuffer, WriteAddr, count);
      WriteAddr +=  count;
      pBuffer += count;

      while (NumOfPage--)
      {
        Flash_WritePage(pBuffer, WriteAddr, sFLASH_SPI_PAGESIZE);
        WriteAddr +=  sFLASH_SPI_PAGESIZE;
        pBuffer += sFLASH_SPI_PAGESIZE;
      }

      if (NumOfSingle != 0)
      {
        Flash_WritePage(pBuffer, WriteAddr, NumOfSingle);
      }
    }
  }
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
  • 198
  • 199
  • 200
  • 201
  • 202
  • 203
  • 204
  • 205
  • 206
  • 207
  • 208
  • 209
  • 210
  • 211
  • 212
  • 213
  • 214
  • 215
  • 216
  • 217
  • 218
  • 219
  • 220
  • 221
  • 222
  • 223
  • 224
  • 225
  • 226
  • 227
  • 228
  • 229
  • 230
  • 231
  • 232
  • 233
  • 234
  • 235
  • 236
  • 237
  • 238
  • 239
  • 240
  • 241
  • 242
  • 243
  • 244
  • 245
  • 246
  • 247
  • 248
  • 249
  • 250
  • 251
  • 252
  • 253
  • 254
  • 255
  • 256
  • 257
  • 258
  • 259
  • 260
  • 261
  • 262
  • 263
  • 264
  • 265
  • 266
  • 267
  • 268
  • 269
  • 270
  • 271
  • 272
  • 273
  • 274
  • 275
  • 276
  • 277
  • 278
  • 279
  • 280
  • 281
  • 282
  • 283
  • 284
  • 285
  • 286
  • 287
  • 288
  • 289
  • 290
  • 291
  • 292
  • 293
  • 294
  • 295
  • 296
  • 297
  • 298
  • 299
  • 300
  • 301
  • 302
  • 303
  • 304
  • 305
  • 306
  • 307
  • 308
  • 309
  • 310
  • 311
  • 312
  • 313
  • 314
  • 315
  • 316
  • 317
  • 318
  • 319
  • 320
  • 321
  • 322
  • 323
  • 324
  • 325
  • 326
  • 327
  • 328
  • 329
  • 330
  • 331
  • 332
  • 333
  • 334
  • 335
  • 336
  • 337
  • 338
  • 339
  • 340
  • 341
  • 342
  • 343
  • 344
  • 345
  • 346
  • 347
  • 348
  • 349
  • 350
  • 351
  • 352
  • 353
  • 354
  • 355
  • 356
  • 357
  • 358
  • 359
  • 360
  • 361
  • 362
  • 363
  • 364
  • 365
  • 366
  • 367
  • 368
  • 369
  • 370
  • 371
  • 372
  • 373
  • 374
  • 375
  • 376
  • 377
  • 378
  • 379
  • 380
  • 381
  • 382
  • 383
  • 384
  • 385
  • 386
  • 387
  • 388
  • 389
  • 390
  • 391
  • 392
  • 393
  • 394
  • 395
  • 396
  • 397
  • 398
  • 399
  • 400
  • 401
  • 402
  • 403
  • 404
  • 405
  • 406
  • 407
  • 408
  • 409
  • 410
  • 411
  • 412
  • 413
  • 414
  • 415

spi.h

#ifndef __SPI_H
#define __SPI_H

#include "stm32f10x.h"
#include "stdio.h"

void SPI2_Config();
void Flash_WritePage(uint8_t *Write_data,uint32_t Write_Local,uint16_t Write_len);
void Flash_ReadData(uint32_t add, uint8_t *data,uint16_t len);
void Flash_SectorErase(uint32_t add);
void Flash_ReadID(void);
void Flash_WaitForWriteEnd(void);
void Flash_ChipClean();
void FLASH_WriteBuffer(uint8_t* pBuffer, uint32_t WriteAddr, uint16_t NumByteToWrite);
void sFLASH_WriteBuffer(uint8_t* pBuffer, uint32_t WriteAddr, uint16_t NumByteToWrite);
#endif

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/一键难忘520/article/detail/832601
推荐阅读
相关标签
  

闽ICP备14008679号