stm32常见的存储器应用

news/2025/2/23 17:51:22

常用 STM32 存储器芯片介绍和应用

STM32 微控制器通常与多种存储器芯片一起工作,以下是几种常见的存储器类型及其应用:

1. 闪存(Flash Memory)

STM32 内部的 闪存 是一种非易失性存储器,广泛用于存储程序代码和常驻数据。

  • 存储特性
    • 具有快速读取速度和较长的寿命(一般可以写入数千到数百万次)。
    • 需要通过 编程操作 将数据写入闪存,通常使用 EEPROM 模拟 技术进行写入。
  • 应用
    • 程序存储:程序代码一般存储在闪存中,STM32 会在复位后从闪存中加载代码。
    • 数据存储:用于存储一些不常更改的数据,如设备的配置信息。

2. SRAM(静态随机存取存储器)

SRAM 是一种内部的存储器,具有更快的访问速度,但需要不断供电,属于易失性存储器。

  • 存储特性
    • 读取速度非常快,并且可以随时写入。
    • 不像闪存那样需要特殊的编程方式,直接写入即可。
  • 应用
    • 数据缓存:通常用于存储临时数据,如计算过程中的中间结果。
    • 堆栈空间:作为栈存储器,处理程序的堆栈和局部变量。
    • DMA(直接内存访问):用于数据传输时作为数据缓冲区。

3. EEPROM(电可擦可编程只读存储器)

STM32 支持通过模拟使用 内部 EEPROM 或外部 EEPROM 存储数据。EEPROM 是一种 非易失性存储器,允许对单个字节进行多次读写操作。

  • 存储特性

    • 适合存储少量的数据。
    • 写入操作较慢,写入次数有限。
  • 应用

    • 配置数据存储:用于存储用户配置、校准数据或偏好设置。
    • 存储日志信息:可用于存储事件日志或传感器数据。

4. 外部存储器(如 SPI Flash, NAND Flash)

在 STM32 系列中,外部存储器通常通过 SPI 或 QSPI 接口 与微控制器连接,广泛用于扩展存储空间。

  • SPI Flash

    • 存储器通过 SPI 接口与 STM32 连接,支持快速读取数据。
    • 主要用于存储大量数据,如应用程序、文件系统等。
  • NAND Flash

    • 用于存储大量的程序和数据,通常用于较大容量存储要求的场景。
    • 常与文件系统(如 FAT 文件系统)配合使用,适用于存储图像、日志、音频等大数据。
  • 应用

    • 固件存储:用于存储操作系统或应用程序的固件。
    • 数据记录:在嵌入式系统中,使用外部存储器存储大数据量,例如传感器数据。

5. SD 卡

STM32 还可以通过 SDIO 接口 与外部 SD 卡 连接,这是一种 大容量存储 设备,广泛用于数据存储和文件系统。

  • 存储特性

    • 支持 FAT32 文件系统,适合存储各种类型的文件。
    • 提供 较高的数据读取速度大容量(如 4GB、32GB 等)。
  • 应用

    • 数据日志记录:如环境监测设备记录温湿度数据。
    • 音频和视频存储:用于存储音频、视频等大数据量文件。

6. 外部 SRAM / DRAM

在一些高级应用中,STM32 也可以连接外部 SRAMDRAM 存储器来提供更大的数据存储空间。

  • 存储特性

    • 外部 SRAM 通常连接到 FSMC(外部存储器控制器),以扩展系统内存。
    • 外部 DRAM 提供更高的存储容量,适用于大数据缓存和视频处理。
  • 应用

    • 大数据缓存:用于高速缓存处理大数据或复杂计算。
    • 图形存储:用于显示处理和图形存储,尤其在显示应用中
    • AT24C02 存储器介绍

    • AT24C02Atmel(现为 Microchip) 生产的一款 2Kbit EEPROM 存储器芯片。它基于 I2C 总线协议,适用于需要小容量存储的嵌入式应用中。

      主要特点:
    • 存储容量:2Kbit,等于 256 字节,可以分为 32 页,每页 8 字节
    • I2C 接口:使用 I2C 总线 进行数据读写,支持 快速模式(400 kHz)标准模式(100 kHz)
    • 写保护:可以通过硬件引脚进行写保护,防止不小心擦写重要数据。
    • 电源电压:一般工作电压为 2.7V 到 5.5V,适配不同的系统需求。
    • 写入时间:一次写入操作通常需要 5ms,读取速度较快。
    • 引脚定义:
    • VCC:电源引脚,通常为 3.3V 或 5V。
    • GND:地引脚。
    • SDA:数据线,I2C 总线的数据传输线。
    • SCL:时钟线,I2C 总线的时钟信号线。
    • WP(写保护):硬件写保护引脚,可用于防止写操作。
    • 工作原理:
    • 写操作

      • 通过 I2C 总线的 发送地址写数据 来向存储器中写入数据。
      • 每个写操作都有一个 字节地址,每次写操作可以写入 1 到 8 字节,这由 页(page) 决定。
    • 读操作

      • 通过发送 设备地址字节地址,然后读取从存储器返回的数据。
      • 读取操作是 顺序读取 的,可以根据需要读取多个字节。
    • AT24C02 常见应用:
    • 存储配置数据:用于存储设备的配置信息、校准数据等。
    • 设备序列号存储:用于嵌入式设备中存储唯一的序列号或标识符。
    • 小型数据记录器:如传感器数据记录和日志存储。
    • MCU 存储扩展:在存储空间有限的情况下,为微控制器提供额外的存储空间。
    • AT24C02 的 I2C 使用示例:
      #include "stm32f10x.h"
      
      #define AT24C02_I2C_ADDRESS 0xA0  // AT24C02 的 I2C 地址
      
      void I2C_Init(void) {
          // 配置 I2C 外设
      }
      
      void AT24C02_Write(uint8_t address, uint8_t data) {
          // 向 AT24C02 写入一个字节的数据
          I2C_Start();  // 启动 I2C
          I2C_Write(AT24C02_I2C_ADDRESS);  // 发送设备地址
          I2C_Write(address);  // 发送写地址
          I2C_Write(data);  // 写入数据
          I2C_Stop();  // 停止 I2C
      }
      
      uint8_t AT24C02_Read(uint8_t address) {
          // 从 AT24C02 读取一个字节的数据
          uint8_t data;
          I2C_Start();  // 启动 I2C
          I2C_Write(AT24C02_I2C_ADDRESS);  // 发送设备地址
          I2C_Write(address);  // 发送读取地址
          I2C_Start();  // 重启 I2C
          I2C_Write(AT24C02_I2C_ADDRESS | 0x01);  // 发送读取地址
          data = I2C_Read_Nack();  // 读取数据
          I2C_Stop();  // 停止 I2C
          return data;
      }
      
      int main(void) {
          // 初始化 I2C
          I2C_Init();
      
          // 写入数据
          AT24C02_Write(0x00, 0x55);  // 向 AT24C02 地址 0x00 写入数据 0x55
      
          // 读取数据
          uint8_t data = AT24C02_Read(0x00);  // 从 AT24C02 地址 0x00 读取数据
      }
      

      总结

    • AT24C02 是一种非常适合低容量存储需求的 EEPROM 存储器,特别适用于通过 I2C 总线 与微控制器进行通信的场景。
    • 它的应用非常广泛,尤其是在需要存储少量非易失性数据的嵌入式系统中,如配置信息、序列号等。

项目:

利用IIC通信对AT24C02芯片进行数据写入,同时读取数据通过串口显示在上位机上面

代码介绍:

#include "stm32f10x.h"                  // Device header
#include   "iic.h"
void iic_GPIO_Config()
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE );
	GPIO_InitTypeDef  iic_GPIO_Initsturt;
	//配置SDA数据线
	
	iic_GPIO_Initsturt.GPIO_Mode =  GPIO_Mode_AF_OD ;
	iic_GPIO_Initsturt.GPIO_Pin =SCL_PIN;   //PB7
	iic_GPIO_Initsturt.GPIO_Speed =GPIO_Speed_50MHz;
	GPIO_Init(IIC_PORT  , &iic_GPIO_Initsturt);
	
	//配置SCL时钟线
	iic_GPIO_Initsturt.GPIO_Mode = GPIO_Mode_AF_OD ;
	iic_GPIO_Initsturt.GPIO_Pin =SDA_PIN;    //PB6
	iic_GPIO_Initsturt.GPIO_Speed =GPIO_Speed_50MHz;
	GPIO_Init(IIC_PORT , &iic_GPIO_Initsturt);
	
}

void iic_Init_Config()
{
	//初始化iic的时钟,配置IIC1的结构体
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1, ENABLE);
	 I2C_InitTypeDef  iic_initsturt;
	 iic_initsturt.I2C_Ack =I2C_Ack_Enable;
	 iic_initsturt.I2C_AcknowledgedAddress =I2C_AcknowledgedAddress_7bit; //配置七位寻从机址 ;
	 iic_initsturt.I2C_ClockSpeed =IIC_Speed ;//速度设置为最大40kbps
	 iic_initsturt.I2C_DutyCycle =I2C_DutyCycle_2;
	 iic_initsturt.I2C_Mode =I2C_Mode_I2C;
	 iic_initsturt.I2C_OwnAddress1 =OwnAddress;//设置主机地址为0
	 I2C_Init( I2C1, &iic_initsturt);
	  I2C_Cmd( I2C1, ENABLE );
	
}
  
   //配置printf函数的usart串口通信
void  usart_GPIO_Config()
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE );
	GPIO_InitTypeDef   usart_GPIO_Initurt;
	//配置usart的TX通信引脚
	usart_GPIO_Initurt.GPIO_Mode =  GPIO_Mode_AF_PP;
	usart_GPIO_Initurt.GPIO_Pin =usart_TXpin;  
  usart_GPIO_Initurt.GPIO_Speed =GPIO_Speed_50MHz;	//PA9       
	GPIO_Init(USART_PORT  , &	usart_GPIO_Initurt);
	
	
	//配置usart的RX通信引脚
	usart_GPIO_Initurt.GPIO_Mode =   GPIO_Mode_IN_FLOATING;
	usart_GPIO_Initurt.GPIO_Pin =usart_RXpin;       //PA10          
	GPIO_Init(USART_PORT  , &	usart_GPIO_Initurt);
	
	
	
}
//配置usart通信结构体USART1,为printf函数做准备
void  usart_Config()
{
	RCC_APB2PeriphClockCmd( RCC_APB2Periph_USART1, ENABLE );
	USART_InitTypeDef usart_Initsturt;
	usart_Initsturt.USART_BaudRate =9600;
	usart_Initsturt.USART_HardwareFlowControl =USART_HardwareFlowControl_None ;   
	usart_Initsturt.USART_Mode =USART_Mode_Rx |USART_Mode_Tx ; 
	usart_Initsturt.USART_Parity = USART_Parity_No;  
	usart_Initsturt.USART_StopBits =USART_StopBits_1  ;
	usart_Initsturt.USART_WordLength =USART_WordLength_8b  ;
	USART_Init(USART1 , &usart_Initsturt);
	USART_Cmd(USART1 , ENABLE );
}
//重定向printf函数,使用fputc找到串口
int fputc(int ch,FILE *f)
{
	 USART_SendData(USART1,(uint8_t)ch);
	 while(USART_GetFlagStatus(USART1, USART_FLAG_TXE)==RESET);
	
	return ch;
}

//重定向scanf函数,使用fgetc找到串口
int fgetc(FILE *f)
{
	while(USART_GetFlagStatus(USART1, USART_FLAG_RXNE)==RESET); 
	
	return (int) USART_ReceiveData(USART1 );
	
}
//配置一个超时反馈函数用于反馈信号
static  uint32_t  iic_timeout_usercallback(uint8_t errocode)
{
	printf("IIC等待超时!errocode=%d",errocode);
	return 0;
	
	
}
//进行iic通信发送数据
  uint32_t iic_WriteData(uint8_t buff, uint8_t WriteAddr)
{
	//产生起始信号
	I2C_GenerateSTART(I2C1, ENABLE);
	//设置等待时间
	uint32_t  iictimer= TIMEOUT_flag;
	//检测EV5事件
	while(I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_MODE_SELECT )==RESET )
	{
		if((iictimer--)==0)
		{
       return  iic_timeout_usercallback(0);
			
		}
		
	}
	//发送地址进行寻址
	I2C_Send7bitAddress(I2C1,  EEPROM_ADDREE  ,  I2C_Direction_Transmitter);
	
	//检测EV6事件
		iictimer= TIMEOUT_flag;
	while(I2C_CheckEvent(I2C1,	I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED )==RESET )
	{
	  if((iictimer--)==0)
	{
		
		return  iic_timeout_usercallback(0);
		
	}
	
}
//发送数据可以寻内部地址,也可进行数据操作
I2C_SendData(I2C1,WriteAddr);

//检测EV8事件
	iictimer= TIMEOUT_flag;
while(I2C_CheckEvent(I2C1,	 I2C_EVENT_MASTER_BYTE_TRANSMITTING  )==RESET )
{
	if((iictimer--)==0)
	{
				return  iic_timeout_usercallback(0);
	}
	
	
}
//发送要写入的数据
I2C_SendData(I2C1,buff);
//检测EV8事件
	iictimer= TIMEOUT_flag;
while(I2C_CheckEvent(I2C1,	 I2C_EVENT_MASTER_BYTE_TRANSMITTING  )==RESET )
{
	if((iictimer--)==0)
	{
				return  iic_timeout_usercallback(0);
	}
}

I2C_GenerateSTOP(I2C1, ENABLE);
return 1;
}
//对ATC02进行读数据
//第一次产生起始信号,对读的地址进行写操作

uint32_t iic_ReadData(uint8_t ADDress, uint8_t *Data,uint8_t number )
{
	I2C_GenerateSTART(I2C1, ENABLE);
  uint32_t  iictimer= TIMEOUT_flag;
	while(I2C_CheckEvent( I2C1,I2C_EVENT_MASTER_MODE_SELECT )==RESET )
	{
		if((iictimer--)==0)
		{
		 iic_timeout_usercallback(0);
		}
		
	}
	 I2C_Send7bitAddress(I2C1,EEPROM_ADDREE , I2C_Direction_Transmitter);
	 iictimer= TIMEOUT_flag;
	while(I2C_CheckEvent( I2C1,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED )==RESET)
	{
		if((iictimer--)==0)
		{
		 iic_timeout_usercallback(0);
		}
		
	}
	I2C_SendData(I2C1,ADDress);
		 iictimer= TIMEOUT_flag;
	while(I2C_CheckEvent( I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTING )==RESET)
	{
		
			if((iictimer--)==0)
		{
		 iic_timeout_usercallback(0);
		}
		
	}
	
	
	//第二次产生起始信号进行读数据操作
		I2C_GenerateSTART(I2C1, ENABLE);
	  iictimer= TIMEOUT_flag;
	while(I2C_CheckEvent( I2C1, I2C_EVENT_MASTER_MODE_SELECT  )==RESET)
	{
			
			if((iictimer--)==0)
		{
		 iic_timeout_usercallback(0);
		}
	}
	  I2C_Send7bitAddress(I2C1, EEPROM_ADDREE, I2C_Direction_Receiver);
	   iictimer= TIMEOUT_flag;
    while(I2C_CheckEvent( I2C1, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED   )==RESET)
		{
			
			if((iictimer--)==0)
		{
		 iic_timeout_usercallback(0);
		}
			
		}
		   while(number--)
			 {
	     *Data= I2C_ReceiveData(I2C1);
		    Data++;   
	  
			 }
	    I2C_GenerateSTOP( I2C1, ENABLE );
			 
			 
			 return 1;
}

这里初始化了usart同时重定向了输入输出函数(scanf,printf)便于打印在上位机,同时对对寻址,读写输入以及起始终止信号进行了时间限制,若运行超时则反馈失败信号在上位机,寻址地址选择的是0xA0写入地址是0x11

#include "main.h"
#include "stm32f10x.h"                  // Device header
#include   "led.h"
#include    "Delay.h"
#include  "iic.h"
#include "pwm.h"
#include  "pwmplus.h"
uint8_t data[10];
  extern TIM_ICUserValue   TIM_CaptureSturt;
int  main()
{

	 iic_GPIO_Config();

   iic_Init_Config(); 
	 printf("这是一个iic通信实验");
	 iic_WriteData(2, 11);
	 delay_ms(100);
   iic_ReadData(11, data,1);
	 delay_ms(100);
	 printf("%d",data[0]);

W25Q 系列芯片介绍

W25Q 系列芯片Winbond 生产的 SPI 接口 Flash 存储器,广泛应用于嵌入式系统、物联网设备、计算机外设等领域。该系列芯片具有较高的读写速度,较低的功耗,并且支持 标准 SPI、双 SPI 和四 SPI 模式,可以为不同应用提供灵活的存储解决方案。

W25Q 系列的主要特点
  • 存储容量:从 1 Mbit128 Mbit 不等,适用于不同的应用需求。
  • 接口类型:支持 SPI 接口,包括标准 SPI、双 SPI 和四 SPI 模式,可以提供更高的数据传输速率。
  • 工作电压:大多数 W25Q 系列 芯片支持 2.7V 至 3.6V 的电压范围。
  • 数据传输速度:支持高速数据传输,某些版本可以达到 104 MHz 的 SPI 时钟速率。
  • 工作温度范围:-40°C 至 85°C,适合工业环境和消费类电子设备。
  • 擦写周期:大多数芯片提供 100 万次擦写周期,适用于频繁数据写入的应用。
W25Q 系列芯片的常见型号
  • W25Q80:8 Mbit (1MB) 存储,适用于较小存储需求的嵌入式系统。
  • W25Q64:64 Mbit (8MB) 存储,常用于较大数据存储和图像存储应用。
  • W25Q128:128 Mbit (16MB) 存储,适用于需要更大存储的应用场景,如图像显示、固件存储等。
主要功能和命令
  • 读取命令
    • 0x03:读取数据命令,适用于从 Flash 中读取数据。
    • 0x0B:高速读取命令,支持更高的读取速度。
    • 0x02:页写命令,写入数据到指定页。
  • 擦除命令
    • 0xC7:全片擦除命令,擦除整个存储器。
    • 0x20:扇区擦除,擦除一个 64 KB 的扇区。
    • 0xD8:块擦除,擦除一个 32 KB 的块。
  • 写入命令
    • 0x06:写使能命令,使能写操作。
    • 0x02:页面写命令,写入数据到指定地址。
W25Q 系列的应用场景
  1. 嵌入式系统:广泛应用于微控制器、单片机、FPGA 等设备中,用于存储程序、配置数据、校准数据等。
  2. 图像存储:在需要存储大尺寸图像的设备中,W25Q64 和 W25Q128 等高容量型号非常适合存储图像数据。
  3. 物联网设备:用于存储传感器数据、配置文件、固件升级包等。
  4. 音频播放设备:如 MP3 播放器、音响系统等,通过 Flash 存储音频文件。
如何使用 W25Q 系列芯片

使用 W25Q 系列芯片,通常需要通过 SPI 接口 进行数据的读写操作。STM32 微控制器等设备通过 SPI 与 W25Q 芯片连接,可以执行以下基本操作:

  1. 初始化 SPI:配置 SPI 接口,设置合适的时序和速度。
  2. 发送命令:通过 SPI 发送控制命令(如读取、写入、擦除命令)。
  3. 数据传输:通过 SPI 发送或接收数据,以实现 Flash 存储器的数据读写。
  4. 擦除和写入:通过特定命令擦除存储区域,并使用页写命令写入数据。
这里w25Q64芯片进行讲解:

W25Q64 是 Winbond 公司生产的一款 串行 Flash 存储器,它属于 W25Q 系列,主要应用于需要高密度存储并且对速度要求较高的嵌入式系统中。它通过 SPI 接口(Serial Peripheral Interface)与微控制器或其他主机设备进行通信。

W25Q64 的主要特性

  1. 存储容量:64 Mbit (8 MB)
  2. 接口类型:SPI(支持标准 SPI、双 SPI 和四 SPI 模式)
  3. 工作电压:2.7V 至 3.6V
  4. 速度
    • 支持最高 104 MHzSPI 时钟速率
    • 支持 数据传输速率
  5. 工作温度范围:-40°C 至 85°C
  6. 存储类型
    • 单一芯片存储,不依赖复杂的外部存储器控制器。
    • 提供 扇区擦除页编程读取 等基本功能。
  7. 数据擦除与写入
    • 提供 扇区擦除(64 KB)块擦除(32 KB) 以及 页写入(256 字节) 等操作。
    • 支持 写保护,可以通过软件控制来限制特定区域的写入。
  8. 特殊功能
    • 四 SPI 模式(高速模式)提供更高的速度。
    • 支持 写保护功能,可以保护数据免受意外写入或修改。

W25Q64 的引脚定义

W25Q64 通过 SPI 接口与主设备进行通信。它的常见引脚包括:

  • CS (Chip Select):选择芯片进行操作,低电平时有效。
  • MISO (Master In Slave Out):主机输入从机输出数据线。
  • MOSI (Master Out Slave In):主机输出从机输入数据线。
  • SCK (Serial Clock):时钟信号,用于同步数据传输。
  • WP (Write Protect):写保护引脚,通常接地以启用写操作。
  • HOLD (Hold):暂停 SPI 总线操作,通常接地。

常用命令

W25Q64 芯片通过 SPI 接口发送命令来进行各种操作。以下是一些常用的命令:

  1. 读取数据命令

    • 0x03:读取数据。
    • 0x0B:高速读取。
    • 0x02:页写命令,写入数据到指定地址。
  2. 写入命令

    • 0x06:写使能,允许进行数据写入操作。
    • 0x02:页面写入命令,支持写入 256 字节数据。
  3. 擦除命令

    • 0xC7:全片擦除。
    • 0x20:扇区擦除,擦除 64 KB 的数据块。
    • 0xD8:块擦除,擦除 32 KB 的数据块。
  4. 其他命令

    • 0x05:读取状态寄存器。
    • 0x35:读取扩展状态寄存器。

如何使用 W25Q64

W25Q64 通常通过 SPI 与 STM32 或其他微控制器进行通信。以下是使用 STM32 微控制器与 W25Q64 进行基本通信的步骤:

  1. 配置 SPI 接口: 使用 STM32 的 SPI 外设来与 W25Q64 进行通信,首先需要配置 SPI 接口。假设我们使用 SPI1,需要配置 MOSIMISOSCKCS 引脚。
void SPI_Config(void) {
    SPI_InitTypeDef SPI_InitStructure;
    GPIO_InitTypeDef GPIO_InitStructure;

    // 使能 SPI 和 GPIO 时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1 | RCC_APB2Periph_GPIOA, ENABLE);

    // 配置 SPI 引脚
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7;  // SCK, MISO, MOSI
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStructure);

    // 配置 SPI1
    SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
    SPI_InitStructure.SPI_Mode = SPI_Mode_Master;
    SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;
    SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low;
    SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge;
    SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;
    SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_16;
    SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
    SPI_Init(SPI1, &SPI_InitStructure);
    SPI_Cmd(SPI1, ENABLE);
}
  1. 发送命令和数据: 要向 W25Q64 发送命令,首先使能写操作,然后通过 SPI 发送命令和数据。
// 发送命令
void W25Q64_SendCommand(uint8_t command) {
    GPIO_ResetBits(GPIOA, GPIO_Pin_4);  // 选择 W25Q64 (CS 低电平)
    SPI_I2S_SendData(SPI1, command);  // 发送命令
    while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_BSY));  // 等待 SPI 完成
    GPIO_SetBits(GPIOA, GPIO_Pin_4);  // 取消选择 W25Q64 (CS 高电平)
}

// 发送数据
void W25Q64_SendData(uint8_t data) {
    GPIO_ResetBits(GPIOA, GPIO_Pin_4);  // 选择 W25Q64 (CS 低电平)
    SPI_I2S_SendData(SPI1, data);  // 发送数据
    while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_BSY));  // 等待 SPI 完成
    GPIO_SetBits(GPIOA, GPIO_Pin_4);  // 取消选择 W25Q64 (CS 高电平)
}
  1. 读取数据: 读取数据时,首先发送读取命令,然后接收数据。
uint8_t W25Q64_ReadData(void) {
    uint8_t received_data = 0;
    GPIO_ResetBits(GPIOA, GPIO_Pin_4);  // 选择 W25Q64 (CS 低电平)
    SPI_I2S_SendData(SPI1, 0x00);  // 发送占位数据以启动 SPI 接收
    while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_BSY));  // 等待 SPI 完成
    received_data = SPI_I2S_ReceiveData(SPI1);  // 读取数据
    GPIO_SetBits(GPIOA, GPIO_Pin_4);  // 取消选择 W25Q64 (CS 高电平)
    return received_data;
}

项目:利用SPI通信对W25Q64芯片进行数据读写,读出来的数据显示在OLED屏上

spi初始化配置:

#include "stm32f10x.h"                  // Device header
#include "spi.h"
void MySPI_Init(){
	 RCC_APB2PeriphClockCmd (RCC_APB2Periph_GPIOA,ENABLE );
	 RCC_APB2PeriphClockCmd ( RCC_APB2Periph_SPI1,ENABLE );
	 GPIO_InitTypeDef  GPIOinitsturt;
	 //配置关于GPIO的NSS输出引脚
	 GPIOinitsturt .GPIO_Mode =GPIO_Mode_Out_PP;
	 GPIOinitsturt .GPIO_Pin =GPIO_Pin_4;
	 GPIOinitsturt .GPIO_Speed = GPIO_Speed_50MHz;
	 GPIO_Init (GPIOA ,&GPIOinitsturt);
	//配置关于GPIO的CLK和MOSI引脚输出
	 GPIOinitsturt .GPIO_Mode =GPIO_Mode_AF_PP ;
	 GPIOinitsturt .GPIO_Pin =GPIO_Pin_5|GPIO_Pin_7;
	 GPIOinitsturt .GPIO_Speed =GPIO_Speed_50MHz;
	 GPIO_Init (GPIOA,&GPIOinitsturt);
	//配置关于GPIO的MISO引脚输出
	 GPIOinitsturt .GPIO_Mode = GPIO_Mode_IPU ;
	 GPIOinitsturt .GPIO_Pin =GPIO_Pin_6;
	 GPIOinitsturt .GPIO_Speed =GPIO_Speed_50MHz;
	 GPIO_Init (GPIOA,&GPIOinitsturt);
	 //初始化SPI外设
	SPI_InitTypeDef   spi_initsturt;
	spi_initsturt.SPI_BaudRatePrescaler =SPI_BaudRatePrescaler_128 ;
	spi_initsturt.SPI_CPHA =SPI_CPHA_1Edge ; 
	spi_initsturt.SPI_CPOL =SPI_CPOL_Low;
	spi_initsturt.SPI_CRCPolynomial =7;
	spi_initsturt.SPI_DataSize =SPI_DataSize_8b ;
	spi_initsturt.SPI_Direction =SPI_Direction_2Lines_FullDuplex ;
	spi_initsturt.SPI_FirstBit =SPI_FirstBit_MSB; 
	spi_initsturt.SPI_Mode =SPI_Mode_Master ;
	spi_initsturt.SPI_NSS =SPI_NSS_Soft ;
  SPI_Init (SPI1,&spi_initsturt);
	SPI_Cmd(SPI1,ENABLE );
	myspi_w_ss(1);
	
	
}
void myspi_w_ss(uint8_t BitValue)
{
	GPIO_WriteBit(GPIOA ,  GPIO_Pin_4, (BitAction) BitValue);
	
}
void myspi_w_sck(uint8_t BitValue)
{
	GPIO_WriteBit(GPIOA ,  GPIO_Pin_5, (BitAction) BitValue);
	
}
	
	void myspi_w_mosi(uint8_t BitValue)
{
	GPIO_WriteBit(GPIOA ,  GPIO_Pin_7, (BitAction) BitValue);
	
}
	void MySPI_Start(void)
	{
		 myspi_w_ss(0);
		
	}
void MySPI_Stop(void )
	{
		 myspi_w_ss(1);
		
	}
	uint8_t  MySPI_SwapByte(uint8_t  byesent)
	{
		
		 while(SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE)==RESET);
		 SPI_I2S_SendData(SPI1, byesent);
		 while(SPI_I2S_GetFlagStatus(SPI1,SPI_I2S_FLAG_RXNE)==RESET);
		 return  SPI_I2S_ReceiveData(SPI1);
		
	}

对W25Q64芯片进行数据读写配置:

#include "w25q64.h"
void W25Q64_Init(void)
{
	MySPI_Init();
}

void W25Q64_ReadID(uint8_t *MID, uint16_t *DID)
{
	MySPI_Start();
	MySPI_SwapByte(W25Q64_JEDEC_ID);
	*MID = MySPI_SwapByte(W25Q64_DUMMY_BYTE);
	*DID = MySPI_SwapByte(W25Q64_DUMMY_BYTE);
	*DID <<= 8;
	*DID |= MySPI_SwapByte(W25Q64_DUMMY_BYTE);
	MySPI_Stop();
}

void W25Q64_WriteEnable(void)
{
	MySPI_Start();
	MySPI_SwapByte(W25Q64_WRITE_ENABLE);
	MySPI_Stop();
}

void W25Q64_WaitBusy(void)
{
	uint32_t Timeout;
	MySPI_Start();
	MySPI_SwapByte(W25Q64_READ_STATUS_REGISTER_1);
	Timeout = 100000;
	while ((MySPI_SwapByte(W25Q64_DUMMY_BYTE) & 0x01) == 0x01)
	{
		Timeout --;
		if (Timeout == 0)
		{
			break;
		}
	}
	MySPI_Stop();
}

void W25Q64_PageProgram(uint32_t Address, uint8_t *DataArray, uint16_t Count)
{
	uint16_t i;
	
	W25Q64_WriteEnable();
	
	MySPI_Start();
	MySPI_SwapByte(W25Q64_PAGE_PROGRAM);
	MySPI_SwapByte(Address >> 16);
	MySPI_SwapByte(Address >> 8);
	MySPI_SwapByte(Address);
	for (i = 0; i < Count; i ++)
	{
		MySPI_SwapByte(DataArray[i]);
	}
	MySPI_Stop();
	
	W25Q64_WaitBusy();
}

void W25Q64_SectorErase(uint32_t Address)
{
	W25Q64_WriteEnable();
	
	MySPI_Start();
	MySPI_SwapByte(W25Q64_SECTOR_ERASE_4KB);
	MySPI_SwapByte(Address >> 16);
	MySPI_SwapByte(Address >> 8);
	MySPI_SwapByte(Address);
	MySPI_Stop();
	
	W25Q64_WaitBusy();
}

void W25Q64_ReadData(uint32_t Address, uint8_t *DataArray, uint32_t Count)
{
	uint32_t i;
	MySPI_Start();
	MySPI_SwapByte(W25Q64_READ_DATA);
	MySPI_SwapByte(Address >> 16);
	MySPI_SwapByte(Address >> 8);
	MySPI_SwapByte(Address);
	for (i = 0; i < Count; i ++)
	{
		DataArray[i] = MySPI_SwapByte(W25Q64_DUMMY_BYTE);
	}
	MySPI_Stop();
}


http://www.niftyadmin.cn/n/5863639.html

相关文章

探索 Peewee:轻量级 Python ORM 简明指南

文章目录 探索 Peewee&#xff1a;轻量级 Python ORM 简明指南主要特点&#xff1a;安装&#xff1a;使用示例&#xff1a;1. 定义模型&#xff1a;2. 初始化数据库&#xff1a;3. 数据操作&#xff08;增、查、改、删&#xff09;&#xff1a;4. 查询构建器&#xff1a;5. 迁移…

Python----PyQt开发(PyQt高级:手搓一个音乐播放器)

一、效果展示 二、设计PyQt界面 本次ui界面设置用到了水平和垂直布局 2.1、设置ui窗口显示大小与位置 self.setWindowTitle(音乐播放器) # 设置窗口标题self.setGeometry(800, 300, 800, 800) # 设置窗口大小和位置 2.2、创建显示歌曲列表控件 # 创建显示歌曲列表的控件 …

uniapp 整合openlayers 编辑图形文件并上传到服务器

引入openlayer依赖 import Map from ol/Map.js // OpenLayers的主要类&#xff0c;用于创建和管理地图 import View from ol/View.js // OpenLayers的视图类&#xff0c;定义地图的视图属性 import TileLayer from ol/layer/Tile.js// OpenLayers的瓦片图层类 import…

verilog中等难度设计实践与ALU设计

Verilog中等难度部分设计实践&#xff08;含ALU算术逻辑单元的设计&#xff09; verilog的中等部分根据Deepseek给出的大纲理应包括时序逻辑verilog设计实践和组合逻辑verilog组合设计实践两个部分。 时序逻辑verilog设计和实践 在 Verilog 中&#xff0c;时序逻辑通常通过 …

计算机考研复试上机07

14、数据结构 1)二叉树 1.常用操作 struct TreeNode{int data;TreeNode *leftChild;TreeNode *rightChild; }; //前序遍历 void PreOrder(TreeNode *root){if(root == NULL) return;visit(root->data);PreOrder(root->leftChild);PreOrder(root->rightChild);ret…

提效10倍:基于Paimon+Dolphin湖仓一体新架构在阿里妈妈品牌业务探索实践

1. 业务背景 阿里妈妈品牌广告数据包括投放引擎、下发、曝光、点击等日志&#xff0c;面向运筹调控、算法特征、分析报表、诊断监控等应用场景&#xff0c;进行了品牌数仓能力建设。随着业务发展&#xff0c;基于Lambda架构的数仓开发模式&#xff0c;缺陷日益突出&#xff1a;…

Windows 上编译 mebedtls 的鸿蒙库

mebedtls 地址&#xff1a;https://github.com/Mbed-TLS/mbedtls 准备工作&#xff1a; clone mebedtls 仓库到本地(tag: mbedtls-2.26.0)鸿蒙工具链(SDK version: v5.0.5) 编译文件修改&#xff1a; 对 CMakeLists.txt 进行修改&#xff0c;主要是关闭了以下几个选项 ENABLE_P…

Spring Boot Validation 接口校验:从零到掌握

在开发 Web 应用时&#xff0c;数据校验是不可忽视的一部分。无论是注册用户信息、提交表单数据&#xff0c;还是处理业务逻辑&#xff0c;数据的有效性和完整性都需要得到保证。Spring Boot 提供了强大的验证功能&#xff0c;基于 Hibernate Validator 框架&#xff0c;通过注…