【STM32】STM32学习笔记-软件SPI读写W25Q64(38)

发布时间:2024年01月24日

00. 目录

01. SPI简介

在大容量产品和互联型产品上,SPI接口可以配置为支持SPI协议或者支持I 2 S音频协议。SPI接口默认工作在SPI方式,可以通过软件把功能从SPI模式切换到I2S模式。

在小容量和中容量产品上,不支持I 2 S音频协议。

串行外设接口(SPI)允许芯片与外部设备以半/全双工、同步、串行方式通信。此接口可以被配置成主模式,并为外部从设备提供通信时钟(SCK)。接口还能以多主配置方式工作。

它可用于多种用途,包括使用一条双向数据线的双线单工同步传输,还可使用CRC校验的可靠通信。

I2S也是一种3引脚的同步串行接口通讯协议。它支持四种音频标准,包括飞利浦I 2 S标准,MSB和LSB对齐标准,以及PCM标准。它在半双工通讯中,可以工作在主和从2种模式下。当它作为主设备时,通过接口向外部的从设备提供时钟信号。

02. W25Q64简介

?W25Qxx系列是一种低成本、小型化、使用简单的非易失性存储器,常应用于数据存储、字库存储、固件程序存储等场景

?存储介质:Nor Flash(闪存)

?时钟频率:80MHz / 160MHz (Dual SPI) / 320MHz (Quad SPI)

?存储容量(24位地址)

03. 软件SPI读写W25Q64接线图

在这里插入图片描述

CS: PA4

CLK: PA5

DO: PA6

DI: PA7

04. 软件SPI读取设备ID示例

spi.h

#ifndef __SPI_H__
#define __SPI_H__

#include "stm32f10x.h"  

void spi_init(void);

void spi_start(void);

void spi_stop(void);

uint8_t spi_swap_byte(uint8_t val);



#endif /*__SPI_H__*/

spi.c

#include "spi.h"

/*
CS: PA4
CLK: PA5
DO: PA6
DI: PA7
*/

//SS写  PA4
void spi_W_SS(uint8_t bitval)
{
	GPIO_WriteBit(GPIOA, GPIO_Pin_4, (BitAction)bitval);
}

//CLK写 PA5
void spi_W_SCK(uint8_t bitval)
{
	GPIO_WriteBit(GPIOA, GPIO_Pin_5, (BitAction)bitval);
}

//DI写 MOSI PA7
void spi_W_MOSI(uint8_t bitval)
{
	GPIO_WriteBit(GPIOA, GPIO_Pin_7, (BitAction)bitval);
}

//DO读   MISO PA6
uint8_t spi_R_MISO(void)
{
	return GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_6);
}



void spi_init(void)
{
	GPIO_InitTypeDef GPIO_InitStruct;
	
	//使能时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
	
	//A4 A5 A7
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
	GPIO_InitStruct.GPIO_Pin = GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_7;
	GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStruct);
	
	//A6
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU;
	GPIO_InitStruct.GPIO_Pin = GPIO_Pin_6;
	GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStruct);	

	spi_W_SS(1);
	spi_W_SCK(0);
}

void spi_start(void)
{
	spi_W_SS(0);
}

void spi_stop(void)
{
	spi_W_SS(1);
}

uint8_t spi_swap_byte(uint8_t val)
{
	uint8_t i = 0;
	
	uint8_t value = 0;
	
	//模式0 第一个边沿移入数据 第二个边沿移出数据
	for (i = 0; i < 8; i++)
	{
		spi_W_MOSI(val & (0x80 >> i));
		spi_W_SCK(1);
		if (spi_R_MISO() == 1)
		{
			value |= 0x80 >> i;
		}
	
		spi_W_SCK(0);
	}
	
	return value;
}

w25q64.h

#ifndef __W25Q64_H__

#define __W25Q64_H__

#include "stm32f10x.h"  


#define W25Q64_WRITE_ENABLE							0x06
#define W25Q64_WRITE_DISABLE						0x04
#define W25Q64_READ_STATUS_REGISTER_1				0x05
#define W25Q64_READ_STATUS_REGISTER_2				0x35
#define W25Q64_WRITE_STATUS_REGISTER				0x01
#define W25Q64_PAGE_PROGRAM							0x02
#define W25Q64_QUAD_PAGE_PROGRAM					0x32
#define W25Q64_BLOCK_ERASE_64KB						0xD8
#define W25Q64_BLOCK_ERASE_32KB						0x52
#define W25Q64_SECTOR_ERASE_4KB						0x20
#define W25Q64_CHIP_ERASE							0xC7
#define W25Q64_ERASE_SUSPEND						0x75
#define W25Q64_ERASE_RESUME							0x7A
#define W25Q64_POWER_DOWN							0xB9
#define W25Q64_HIGH_PERFORMANCE_MODE				0xA3
#define W25Q64_CONTINUOUS_READ_MODE_RESET			0xFF
#define W25Q64_RELEASE_POWER_DOWN_HPM_DEVICE_ID		0xAB
#define W25Q64_MANUFACTURER_DEVICE_ID				0x90
#define W25Q64_READ_UNIQUE_ID						0x4B
#define W25Q64_JEDEC_ID								0x9F
#define W25Q64_READ_DATA							0x03
#define W25Q64_FAST_READ							0x0B
#define W25Q64_FAST_READ_DUAL_OUTPUT				0x3B
#define W25Q64_FAST_READ_DUAL_IO					0xBB
#define W25Q64_FAST_READ_QUAD_OUTPUT				0x6B
#define W25Q64_FAST_READ_QUAD_IO					0xEB
#define W25Q64_OCTAL_WORD_READ_QUAD_IO				0xE3

#define W25Q64_DUMMY_BYTE							0xFF



void W25Q64_init(void);

void W25Q64_read_id(uint8_t *mid, uint16_t *did);


#endif /*__W25Q64_H__*/

w25q64.c

#include "w25q64.h"
#include "spi.h"

void W25Q64_init(void)
{
	spi_init();
}

void W25Q64_read_id(uint8_t *mid, uint16_t *did)
{
	spi_start();
	spi_swap_byte(W25Q64_JEDEC_ID);
	
	*mid = spi_swap_byte(W25Q64_DUMMY_BYTE);
	*did = spi_swap_byte(W25Q64_DUMMY_BYTE);
	*did <<= 8;
	*did |= spi_swap_byte(W25Q64_DUMMY_BYTE);
	
	spi_stop();
}


main.c

#include "stm32f10x.h"

#include "delay.h"
#include "oled.h"
#include "w25q64.h"


 int main(void)
 {	
	 uint8_t mid;
	 uint16_t did;

	 //初始化
	 OLED_Init();

	 W25Q64_init();

	 //显示一个字符
	 //OLED_ShowChar(1, 1, 'A');
	 //显示字符串
	 //OLED_ShowString(1, 3, "SPI Test");

	 OLED_ShowString(1, 1, "MID:   DID:");
	 OLED_ShowString(2, 1, "W:");
	 OLED_ShowString(3, 1, "R:");
	 
	 W25Q64_read_id(&mid, &did);
	 OLED_ShowHexNum(1, 5, mid, 2);
	 OLED_ShowHexNum(1, 12, did, 4);
	 
	 
	 while(1)
	 {
		 
	 }
	 
	 return 0;
 }

05. 软件SPI读写W25Q64Flash示例

spi.h

#ifndef __SPI_H__
#define __SPI_H__

#include "stm32f10x.h"  

void spi_init(void);

void spi_start(void);

void spi_stop(void);

uint8_t spi_swap_byte(uint8_t val);



#endif /*__SPI_H__*/


spi.c

#include "spi.h"

/*
CS: PA4
CLK: PA5
DO: PA6
DI: PA7
*/

//SS写  PA4
void spi_W_SS(uint8_t bitval)
{
	GPIO_WriteBit(GPIOA, GPIO_Pin_4, (BitAction)bitval);
}

//CLK写 PA5
void spi_W_SCK(uint8_t bitval)
{
	GPIO_WriteBit(GPIOA, GPIO_Pin_5, (BitAction)bitval);
}

//DI写 MOSI PA7
void spi_W_MOSI(uint8_t bitval)
{
	GPIO_WriteBit(GPIOA, GPIO_Pin_7, (BitAction)bitval);
}

//DO读   MISO PA6
uint8_t spi_R_MISO(void)
{
	return GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_6);
}



void spi_init(void)
{
	GPIO_InitTypeDef GPIO_InitStruct;
	
	//使能时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
	
	//A4 A5 A7
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
	GPIO_InitStruct.GPIO_Pin = GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_7;
	GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStruct);
	
	//A6
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU;
	GPIO_InitStruct.GPIO_Pin = GPIO_Pin_6;
	GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStruct);	

	spi_W_SS(1);
	spi_W_SCK(0);
}

void spi_start(void)
{
	spi_W_SS(0);
}

void spi_stop(void)
{
	spi_W_SS(1);
}

uint8_t spi_swap_byte(uint8_t val)
{
	uint8_t i = 0;
	
	uint8_t value = 0;
	
	//模式0 第一个边沿移入数据 第二个边沿移出数据
	for (i = 0; i < 8; i++)
	{
		spi_W_MOSI(val & (0x80 >> i));
		spi_W_SCK(1);
		if (spi_R_MISO() == 1)
		{
			value |= 0x80 >> i;
		}
	
		spi_W_SCK(0);
	}
	
	return value;
}

w25q64.h

#ifndef __W25Q64_H__

#define __W25Q64_H__

#include "stm32f10x.h"  


#define W25Q64_WRITE_ENABLE							0x06
#define W25Q64_WRITE_DISABLE						0x04
#define W25Q64_READ_STATUS_REGISTER_1				0x05
#define W25Q64_READ_STATUS_REGISTER_2				0x35
#define W25Q64_WRITE_STATUS_REGISTER				0x01
#define W25Q64_PAGE_PROGRAM							0x02
#define W25Q64_QUAD_PAGE_PROGRAM					0x32
#define W25Q64_BLOCK_ERASE_64KB						0xD8
#define W25Q64_BLOCK_ERASE_32KB						0x52
#define W25Q64_SECTOR_ERASE_4KB						0x20
#define W25Q64_CHIP_ERASE							0xC7
#define W25Q64_ERASE_SUSPEND						0x75
#define W25Q64_ERASE_RESUME							0x7A
#define W25Q64_POWER_DOWN							0xB9
#define W25Q64_HIGH_PERFORMANCE_MODE				0xA3
#define W25Q64_CONTINUOUS_READ_MODE_RESET			0xFF
#define W25Q64_RELEASE_POWER_DOWN_HPM_DEVICE_ID		0xAB
#define W25Q64_MANUFACTURER_DEVICE_ID				0x90
#define W25Q64_READ_UNIQUE_ID						0x4B
#define W25Q64_JEDEC_ID								0x9F
#define W25Q64_READ_DATA							0x03
#define W25Q64_FAST_READ							0x0B
#define W25Q64_FAST_READ_DUAL_OUTPUT				0x3B
#define W25Q64_FAST_READ_DUAL_IO					0xBB
#define W25Q64_FAST_READ_QUAD_OUTPUT				0x6B
#define W25Q64_FAST_READ_QUAD_IO					0xEB
#define W25Q64_OCTAL_WORD_READ_QUAD_IO				0xE3

#define W25Q64_DUMMY_BYTE							0xFF



void W25Q64_init(void);

void W25Q64_read_id(uint8_t *mid, uint16_t *did);

//写使能
void W25Q64_write_enable(void);

//等待 直到空闲
void W25Q64_wait_busy(void);

void W25Q64_sector_erase(uint32_t addr);

void W25Q64_page_program(uint32_t addr, uint8_t *arr, uint16_t len);

void W25Q64_read_data(uint32_t addr, uint8_t *arr, uint16_t len);


#endif /*__W25Q64_H__*/

w25q64.c

#include "w25q64.h"
#include "spi.h"

void W25Q64_init(void)
{
	spi_init();
}

void W25Q64_read_id(uint8_t *mid, uint16_t *did)
{
	spi_start();
	spi_swap_byte(W25Q64_JEDEC_ID);
	
	*mid = spi_swap_byte(W25Q64_DUMMY_BYTE);
	*did = spi_swap_byte(W25Q64_DUMMY_BYTE);
	*did <<= 8;
	*did |= spi_swap_byte(W25Q64_DUMMY_BYTE);
	
	spi_stop();
}


void W25Q64_write_enable(void)
{
	spi_start();

	spi_swap_byte(W25Q64_WRITE_ENABLE);
	
	spi_stop();
}


void W25Q64_wait_busy(void)
{
	uint32_t timeout;
	spi_start();
	
	spi_swap_byte(W25Q64_READ_STATUS_REGISTER_1);	
	
	timeout = 100000;
	while((spi_swap_byte(W25Q64_DUMMY_BYTE) & 0x1) == 0x01)
	{
		timeout--;
		if (0 == timeout)
		{
			break;
		}
	}
	
	spi_stop();
}


void W25Q64_page_program(uint32_t addr, uint8_t *arr, uint16_t len)
{
	uint8_t i;
	
	W25Q64_write_enable();
	
	spi_start();
	
	spi_swap_byte(W25Q64_PAGE_PROGRAM);	
	
	spi_swap_byte(addr >> 16);
	spi_swap_byte(addr >> 8);
	spi_swap_byte(addr);	

	for (i = 0; i < len; i++)
	{
		spi_swap_byte(arr[i]);
	}
	
	spi_stop();
	
	W25Q64_wait_busy();
}


void W25Q64_sector_erase(uint32_t addr)
{
	W25Q64_write_enable();
	
	spi_start();
	
	spi_swap_byte(W25Q64_SECTOR_ERASE_4KB);	
	
	spi_swap_byte(addr >> 16);
	spi_swap_byte(addr >> 8);
	spi_swap_byte(addr);

	spi_stop();
	
	W25Q64_wait_busy();
	
}

void W25Q64_read_data(uint32_t addr, uint8_t *arr, uint16_t len)
{
	uint8_t i = 0;
	
	spi_start();
	
	spi_swap_byte(W25Q64_READ_DATA);	
	
	spi_swap_byte(addr >> 16);
	spi_swap_byte(addr >> 8);
	spi_swap_byte(addr);
	
	for (i = 0; i < len; i++)
	{
		arr[i] = spi_swap_byte(W25Q64_DUMMY_BYTE);
	}

	spi_stop();
}


main.c

#include "stm32f10x.h"

#include "delay.h"
#include "oled.h"
#include "w25q64.h"


 int main(void)
 {	
	 uint8_t mid;
	 uint16_t did;
	 
	 uint8_t array_w[4] = {0x11, 0x22, 0x33, 0x44};
	 uint8_t array_r[4];

	 //初始化
	 OLED_Init();

	 W25Q64_init();

	 //显示一个字符
	 //OLED_ShowChar(1, 1, 'A');
	 //显示字符串
	 //OLED_ShowString(1, 3, "SPI Test");

	 OLED_ShowString(1, 1, "MID:   DID:");
	 OLED_ShowString(2, 1, "W:");
	 OLED_ShowString(3, 1, "R:");
	 
	 W25Q64_read_id(&mid, &did);
	 OLED_ShowHexNum(1, 5, mid, 2);
	 OLED_ShowHexNum(1, 12, did, 4);
	 
	 //擦除扇区
	 W25Q64_sector_erase(0x0);
	 
	 //写扇区
	 W25Q64_page_program(0x0, array_w, 4);
	 
	 //读数据
	 W25Q64_read_data(0x0, array_r, 4);
	 
	OLED_ShowHexNum(2, 3, array_w[0], 2);
	OLED_ShowHexNum(2, 6, array_w[1], 2);
	OLED_ShowHexNum(2, 9, array_w[2], 2);
	OLED_ShowHexNum(2, 12, array_w[3], 2);	 

	OLED_ShowHexNum(3, 3, array_r[0], 2);
	OLED_ShowHexNum(3, 6, array_r[1], 2);
	OLED_ShowHexNum(3, 9, array_r[2], 2);
	OLED_ShowHexNum(3, 12, array_r[3], 2);			 
		 
		 
	 while(1)
	 {
		 
	 }
	 
	 return 0;
 }

06. 示例下载

30-软件SPI.rar

07. 附录

参考: 【STM32】江科大STM32学习笔记汇总

文章来源:https://blog.csdn.net/u010249597/article/details/135825705
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。