常用 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 也可以连接外部 SRAM 或 DRAM 存储器来提供更大的数据存储空间。
-
存储特性:
- 外部 SRAM 通常连接到 FSMC(外部存储器控制器),以扩展系统内存。
- 外部 DRAM 提供更高的存储容量,适用于大数据缓存和视频处理。
-
应用:
- 大数据缓存:用于高速缓存处理大数据或复杂计算。
- 图形存储:用于显示处理和图形存储,尤其在显示应用中
-
AT24C02 存储器介绍
-
-
-
-
-
-
-
AT24C02 是 Atmel(现为 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 Mbit 到 128 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 系列的应用场景:
- 嵌入式系统:广泛应用于微控制器、单片机、FPGA 等设备中,用于存储程序、配置数据、校准数据等。
- 图像存储:在需要存储大尺寸图像的设备中,W25Q64 和 W25Q128 等高容量型号非常适合存储图像数据。
- 物联网设备:用于存储传感器数据、配置文件、固件升级包等。
- 音频播放设备:如 MP3 播放器、音响系统等,通过 Flash 存储音频文件。
如何使用 W25Q 系列芯片
使用 W25Q 系列芯片,通常需要通过 SPI 接口 进行数据的读写操作。STM32 微控制器等设备通过 SPI 与 W25Q 芯片连接,可以执行以下基本操作:
- 初始化 SPI:配置 SPI 接口,设置合适的时序和速度。
- 发送命令:通过 SPI 发送控制命令(如读取、写入、擦除命令)。
- 数据传输:通过 SPI 发送或接收数据,以实现 Flash 存储器的数据读写。
- 擦除和写入:通过特定命令擦除存储区域,并使用页写命令写入数据。
这里w25Q64芯片进行讲解:
W25Q64 是 Winbond 公司生产的一款 串行 Flash 存储器,它属于 W25Q 系列,主要应用于需要高密度存储并且对速度要求较高的嵌入式系统中。它通过 SPI 接口(Serial Peripheral Interface)与微控制器或其他主机设备进行通信。
W25Q64 的主要特性
- 存储容量:64 Mbit (8 MB)
- 接口类型:SPI(支持标准 SPI、双 SPI 和四 SPI 模式)
- 工作电压:2.7V 至 3.6V
- 速度:
- 支持最高 104 MHz 的 SPI 时钟速率。
- 支持 数据传输速率。
- 工作温度范围:-40°C 至 85°C
- 存储类型:
- 单一芯片存储,不依赖复杂的外部存储器控制器。
- 提供 扇区擦除、页编程、读取 等基本功能。
- 数据擦除与写入:
- 提供 扇区擦除(64 KB)、块擦除(32 KB) 以及 页写入(256 字节) 等操作。
- 支持 写保护,可以通过软件控制来限制特定区域的写入。
- 特殊功能:
- 四 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 接口发送命令来进行各种操作。以下是一些常用的命令:
-
读取数据命令:
0x03
:读取数据。0x0B
:高速读取。0x02
:页写命令,写入数据到指定地址。
-
写入命令:
0x06
:写使能,允许进行数据写入操作。0x02
:页面写入命令,支持写入 256 字节数据。
-
擦除命令:
0xC7
:全片擦除。0x20
:扇区擦除,擦除 64 KB 的数据块。0xD8
:块擦除,擦除 32 KB 的数据块。
-
其他命令:
0x05
:读取状态寄存器。0x35
:读取扩展状态寄存器。
如何使用 W25Q64
W25Q64 通常通过 SPI 与 STM32 或其他微控制器进行通信。以下是使用 STM32 微控制器与 W25Q64 进行基本通信的步骤:
- 配置 SPI 接口: 使用 STM32 的 SPI 外设来与 W25Q64 进行通信,首先需要配置 SPI 接口。假设我们使用 SPI1,需要配置
MOSI
、MISO
、SCK
和CS
引脚。
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);
}
- 发送命令和数据: 要向 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 高电平)
}
- 读取数据: 读取数据时,首先发送读取命令,然后接收数据。
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();
}