STM32教程第十八讲:SPI通信

目录

  • 需求
  • 一、SPI概要
  • 二、SPI配置
  • 1.开时钟
  • 2.配置IO
  • 3.配置&使能SPI
  • 三、FLash操作函数
  • 1.SPI发送数据
  • 2.FLASH写使能
  • 3.FLASH等待操作完成
  • 4.FLASH页写操作
  • 5.FLASH读操作
  • 6.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);
    

    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避免通信出错
    

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

    3.配置&使能SPI


    参考手册中的步骤如下:


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

    SPI的模式0 CPHA=0,CPOL=0 上升沿采样、下降沿接受
    SPI的模式1 CPHA=0,CPOL=1 下降沿采样、上升沿接受
    SPI的模式2 CPHA=1,CPOL=0 下降沿采样、上升沿接受
    SPI的模式3 CPHA=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);
    

    本此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;
    }
    
    

    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);	
    }
    
    

    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);	
    }
    
    

    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();
    }
    

    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);	
    }
    

    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();
    }
    

    四、需求实现

    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;
    }
    
    
    
    

    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);
          }
        }
      }
    }
    
    

    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
    
    

    作者:小白橘颂

    物联沃分享整理
    物联沃-IOTWORD物联网 » STM32教程第十八讲:SPI通信

    发表回复