当前位置: 首页 > news >正文

stm32常见的存储器应用

常用 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();  // 启动 I2CI2C_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();  // 启动 I2CI2C_Write(AT24C02_I2C_ADDRESS);  // 发送设备地址I2C_Write(address);  // 发送读取地址I2C_Start();  // 重启 I2CI2C_Write(AT24C02_I2C_ADDRESS | 0x01);  // 发送读取地址data = I2C_Read_Nack();  // 读取数据I2C_Stop();  // 停止 I2Creturn data;
      }int main(void) {// 初始化 I2CI2C_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;   //PB7iic_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;    //PB6iic_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 ;//速度设置为最大40kbpsiic_initsturt.I2C_DutyCycle =I2C_DutyCycle_2;iic_initsturt.I2C_Mode =I2C_Mode_I2C;iic_initsturt.I2C_OwnAddress1 =OwnAddress;//设置主机地址为0I2C_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, MOSIGPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIO_InitStructure);// 配置 SPI1SPI_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();
}

相关文章:

stm32常见的存储器应用

常用 STM32 存储器芯片介绍和应用 STM32 微控制器通常与多种存储器芯片一起工作&#xff0c;以下是几种常见的存储器类型及其应用&#xff1a; 1. 闪存&#xff08;Flash Memory&#xff09; STM32 内部的 闪存 是一种非易失性存储器&#xff0c;广泛用于存储程序代码和常驻…...

如何使用3D高斯分布进行环境建模

使用3D高斯分布来实现建模&#xff0c;主要是通过高斯分布的概率特性来描述空间中每个点的几何位置和不确定性。具体来说&#xff0c;3D高斯分布被用来表示点云数据中的每一个点或体素&#xff08;voxel&#xff09;的空间分布和不确定性&#xff0c;而不是单纯地存储每个点的坐…...

三级分类bug解决

文章目录 前端后端 前端 <!DOCTYPE html> <html xmlns:th"http://www.thymeleaf.org" lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0&q…...

AxiosError: Network Error

不知怎么的&#xff0c;项目还在开发阶段&#xff0c;之前还好好的&#xff0c;玩儿了两天再一打开发现页面无法显示数据了&#xff0c;报错如下&#xff1a; 我以为是后端出问题了&#xff0c;但是后端控制台无报错&#xff0c;又用postman测试了一下&#xff0c;可以获取到数…...

CDefFolderMenu_MergeMenu函数分析之添加了分割线和属性菜单项两项

CDefFolderMenu_MergeMenu函数分析之添加了分割线和属性菜单项两项 第一部分&#xff1a; void CDefFolderMenu_MergeMenu(HINSTANCE hinst, UINT idMainMerge, UINT idPopupMerge, QCMINFO *pqcm) { UINT idMax pqcm->idCmdFirst; if (idMainMerge) { HME…...

mysql的源码包安装

安装方式一&#xff1a;&#xff08;编译好的直接安装&#xff09; 1.添加一块10G的硬盘&#xff0c;给root逻辑卷扩容 &#xff08;下面安装方式二有&#xff0c;一模一样的装就行&#xff0c;我就不写了&#xff0c;再写的话篇幅就太长了&#xff09; 2.下载编译好的源码包…...

win11系统无法打开软件_组策略无法打开_gpedit.msc不生效_为了对电脑进行保护,已经阻止此应用---Windows工作笔记057

碰到这个问题挺麻烦的,要用的软件打不开了. 其实解决方法就是去组策略中修改一个策略就可以了,但是: 先来说: 而且,使用cmd输入的gpedit.msc也打不开了. 这个怎么解决? @echo off pushd "%~dp0"dir /b C:\Windows\servicing\Packages\Microsoft-Windows-GroupPo…...

【JAVA】io流之缓冲流

①BufferedInputStream、BufferedOutputStream&#xff08;适合读写非普通文本文件&#xff09; ②BufferedReader、BufferedWriter&#xff08;适合读写普通文本文件。&#xff09; 缓冲流的读写速度快&#xff0c;原理是&#xff1a;在内存中准备了一个缓存。读的时候从缓存中…...

from flask_session import Session 为什么是Session(app)这么用?

在 Flask 中&#xff0c;from flask_session import Session 和 Session(app) 的用法是为了配置和使用 Flask-Session 扩展&#xff0c;将用户的会话&#xff08;Session&#xff09;数据存储到服务器端&#xff08;如 Redis、数据库或文件系统&#xff09;&#xff0c;而不是默…...

AI赋能的未来城市:如何用智能化提升生活质量?

这会是我们憧憬的未来城市吗&#xff1f; 随着技术的不断进步和城市化进程的加速&#xff0c;现代城市面临着诸多挑战——交通拥堵、环境污染、能源消耗、人口老龄化等问题愈发突出。为了应对这些挑战&#xff0c;建设智慧城市已成为全球发展的重要趋势。在这一进程中&#xf…...

【Go】Go wire 依赖注入

1. wire 简介 wire 是一个 Golang 的依赖注入框架&#xff08;类比 Spring 框架提供的依赖注入功能&#xff09; ⭐ 官方文档&#xff1a;https://github.com/google/wire 这里关乎到编程世界当中一条好用的设计原则&#xff1a;A用到了B&#xff0c;那么B一定是通过依赖注入的…...

深度集成DeepSeek与Java开发:智能编码新纪元全攻略 [特殊字符]

一、DeepSeek&#xff1a;Java开发者的第二大脑 &#x1f9e0; 1.1 传统开发痛点VS智能开发体验 传统开发DeepSeek智能辅助效率提升对比手动编写重复代码一键生成模板代码代码量减少70%↑调试全靠断点日志智能定位缺陷根源问题排查时间缩短60%↓文档维护耗时费力自动生成更新…...

WEB前端将指定DOM生成图片并下载最佳实践(html2canvas)

前言&#xff1a; html2canvas 是一个 JavaScript 库&#xff0c;其主要作用是将 HTML 元素或其部分内容渲染为 Canvas 图像。通过它&#xff0c;开发者可以将网页中的任意 DOM 元素&#xff08;包括文本、图片、样式等&#xff09;转换为图片格式&#xff08;如 PNG 或 JPEG&…...

掌握.NET Core后端发布流程,如何部署后端应用?

无论你是刚接触.NET Core的新手还是已有经验的开发者&#xff0c;在这篇文章中你将会学习到一系列实用的发布技巧与最佳实践&#xff0c;帮助你高效顺利地将.NET Core后端应用部署到生产环境中 目录 程序发布操作 Docker容器注册表 文件夹发布 导入配置文件 网站运行操作 …...

深度学习学习笔记(34周)

目录 摘要 Abstracts 简介 Hourglass Module&#xff08;Hourglass 模块&#xff09; 网络结构 Intermediate Supervision&#xff08;中间监督&#xff09; 训练过程细节 评测结果 摘要 本周阅读了《Stacked Hourglass Networks for Human Pose Estimation》&#xf…...

C++ 设计模式-备忘录模式

游戏存档实现&#xff0c;包括撤销/重做、持久化存储、版本控制和内存管理 #include <iostream> #include <memory> #include <deque> #include <stack> #include <chrono> #include <fstream> #include <sstream> #include <ct…...

TOGAF之架构标准规范-信息系统架构 | 应用架构

TOGAF是工业级的企业架构标准规范&#xff0c;信息系统架构阶段是由数据架构阶段以及应用架构阶段构成&#xff0c;本文主要描述信息系统架构阶段中的应用架构阶段。 如上所示&#xff0c;信息系统架构&#xff08;Information Systems Architectures&#xff09;在TOGAF标准规…...

第一届网谷杯

统计四场的所有题目&#xff08;共计12题&#xff0c;四场比赛一共上了21题【包括换题】&#xff09; 随便记记&#xff0c;以免老题复用&#xff08;已经复用了&#xff09; Web 文件包含 1 伪协议 http://120.202.175.143:8011/?cphp://filter/convert.base64-encode/reso…...

Linux(ubuntu) GPU CUDA 构建Docker镜像

一、创建Dockerfile FROM ubuntu:20.04#非交互式&#xff0c;以快速运行自动化任务或脚本&#xff0c;无需图形界面 ENV DEBIAN_FRONTENDnoninteractive# 安装基础工具 RUN apt-get update && apt-get install -y \curl \wget \git \build-essential \software-proper…...

mysql -DQL语句和DCL语句

DQL 数据查询语言&#xff08;Data Query Language&#xff0c;DQL&#xff09;是数据库操作语言的重要组成部分&#xff0c;主要用于从数据库中检索数据&#xff0c;核心关键字为SELECT。以下从语法结构、常见操作及示例等方面详细介绍&#xff1a; 语法结构 DQL 的标准语法…...

【根据当天日期输出明天的日期(需对闰年做判定)。】2022-5-15

缘由根据当天日期输出明天的日期(需对闰年做判定)。日期类型结构体如下&#xff1a; struct data{ int year; int month; int day;};-编程语言-CSDN问答 struct mdata{ int year; int month; int day; }mdata; int 天数(int year, int month) {switch (month){case 1: case 3:…...

设计模式和设计原则回顾

设计模式和设计原则回顾 23种设计模式是设计原则的完美体现,设计原则设计原则是设计模式的理论基石, 设计模式 在经典的设计模式分类中(如《设计模式:可复用面向对象软件的基础》一书中),总共有23种设计模式,分为三大类: 一、创建型模式(5种) 1. 单例模式(Sing…...

Golang 面试经典题:map 的 key 可以是什么类型?哪些不可以?

Golang 面试经典题&#xff1a;map 的 key 可以是什么类型&#xff1f;哪些不可以&#xff1f; 在 Golang 的面试中&#xff0c;map 类型的使用是一个常见的考点&#xff0c;其中对 key 类型的合法性 是一道常被提及的基础却很容易被忽视的问题。本文将带你深入理解 Golang 中…...

【WiFi帧结构】

文章目录 帧结构MAC头部管理帧 帧结构 Wi-Fi的帧分为三部分组成&#xff1a;MAC头部frame bodyFCS&#xff0c;其中MAC是固定格式的&#xff0c;frame body是可变长度。 MAC头部有frame control&#xff0c;duration&#xff0c;address1&#xff0c;address2&#xff0c;addre…...

2025年能源电力系统与流体力学国际会议 (EPSFD 2025)

2025年能源电力系统与流体力学国际会议&#xff08;EPSFD 2025&#xff09;将于本年度在美丽的杭州盛大召开。作为全球能源、电力系统以及流体力学领域的顶级盛会&#xff0c;EPSFD 2025旨在为来自世界各地的科学家、工程师和研究人员提供一个展示最新研究成果、分享实践经验及…...

Docker 运行 Kafka 带 SASL 认证教程

Docker 运行 Kafka 带 SASL 认证教程 Docker 运行 Kafka 带 SASL 认证教程一、说明二、环境准备三、编写 Docker Compose 和 jaas文件docker-compose.yml代码说明&#xff1a;server_jaas.conf 四、启动服务五、验证服务六、连接kafka服务七、总结 Docker 运行 Kafka 带 SASL 认…...

连锁超市冷库节能解决方案:如何实现超市降本增效

在连锁超市冷库运营中&#xff0c;高能耗、设备损耗快、人工管理低效等问题长期困扰企业。御控冷库节能解决方案通过智能控制化霜、按需化霜、实时监控、故障诊断、自动预警、远程控制开关六大核心技术&#xff0c;实现年省电费15%-60%&#xff0c;且不改动原有装备、安装快捷、…...

论文浅尝 | 基于判别指令微调生成式大语言模型的知识图谱补全方法(ISWC2024)

笔记整理&#xff1a;刘治强&#xff0c;浙江大学硕士生&#xff0c;研究方向为知识图谱表示学习&#xff0c;大语言模型 论文链接&#xff1a;http://arxiv.org/abs/2407.16127 发表会议&#xff1a;ISWC 2024 1. 动机 传统的知识图谱补全&#xff08;KGC&#xff09;模型通过…...

【JavaSE】多线程基础学习笔记

多线程基础 -线程相关概念 程序&#xff08;Program&#xff09; 是为完成特定任务、用某种语言编写的一组指令的集合简单的说:就是我们写的代码 进程 进程是指运行中的程序&#xff0c;比如我们使用QQ&#xff0c;就启动了一个进程&#xff0c;操作系统就会为该进程分配内存…...

es6+和css3新增的特性有哪些

一&#xff1a;ECMAScript 新特性&#xff08;ES6&#xff09; ES6 (2015) - 革命性更新 1&#xff0c;记住的方法&#xff0c;从一个方法里面用到了哪些技术 1&#xff0c;let /const块级作用域声明2&#xff0c;**默认参数**&#xff1a;函数参数可以设置默认值。3&#x…...