STM32F407VET6使用SPI访问Flash数据返回0xff(先写入数据,再读取数据,却返回0xff,但是通过debug却可以正常输出)

这篇具有很好参考价值的文章主要介绍了STM32F407VET6使用SPI访问Flash数据返回0xff(先写入数据,再读取数据,却返回0xff,但是通过debug却可以正常输出)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

STM32F407VET6使用SPI访问Flash数据返回0xff(先写入数据,再读取数据,却返回0xff,但是通过debug却可以正常输出)

看了野火的STM32F103VET6板子的SPI操作Flash的视频,用了自己的STM32F407VET6板子试了一下,出现了点问题,在网上看了很久也没有找出原因,现在问题解决了,就写一篇,如果大家有这种用F4的板子操作的情况,可以参考一下。

出现题目括号中所说的问题,我开始以为是我读取的太快导致数据还有写入进去,就读取了,所以导致读到的数据都是未初始化的flash地址数据0xff。后来加了延时函数也是不行,但是通过debug模式却可以正常读取到写入的数据。其实这个问题很简单,就是遗漏一点。在使用SPI读写flash数据的时候,首先需要进行扇区数据的擦除(擦除的函数,看野火的应该都知道),虽然其他的所有的写入函数中都已经添加了等待写入完成操作;但是,并没有位擦除数据的函数写入一个等待写入完成的一步。因为对数据扇区的擦除操作同样也是写入操作,所以必须需要加上等待写入完成操作,否则就会发现在读取数据的时候全部返回0xff。

下面附上代码(STM32F407VET6的板子,参考野火F103写的,代码在复制的时候格式自己乱了,不过问题不大)。

spi_flash.h

#ifndef _SPI_FLASH_
#define _SPI_FLASH_
#include "stm32f4xx.h"
#include "delay.h"

//spi参数定义
#define FLASH_SPIX                	SPI1
#define FLASH_SPI_APBX_CLOCK      	RCC_APB2PeriphClockCmd//spi1是挂在再APB2上,时钟线使能
#define FLASH_SPI_CLK 							RCC_APB2Periph_SPI1//RCC_APB2PeriphClockCmd的使能参数
#define FLASH_SPI_GPIO_APBX_CLOCK 	RCC_AHB1PeriphClockCmd//给GPIO使能
#define FLASH_SPI_GPIO_CLK        	RCC_AHB1Periph_GPIOB//flash的GPIO引脚是GPIOB 挂在再AHB1上的

#define FLASH_SPI_SCK_PORT					GPIOB
#define FLASH_SPI_SCK_PIN						GPIO_Pin_3

#define FLASH_SPI_MOSI_PORT					GPIOB						
#define FLASH_SPI_MOSI_PIN					GPIO_Pin_5

#define FLASH_SPI_MISO_PORT					GPIOB
#define FLASH_SPI_MISO_PIN					GPIO_Pin_4

#define FLASH_SPI_CS_PORT						GPIOB
#define FLASH_SPI_CS_PIN						GPIO_Pin_0

#define FLASH_SPI_CS_HIGH           GPIO_SetBits(FLASH_SPI_CS_PORT,FLASH_SPI_CS_PIN);
#define FLASH_SPI_CS_LOW           	GPIO_ResetBits(FLASH_SPI_CS_PORT,FLASH_SPI_CS_PIN);


#define CHAN                         0x00//随机数据
#define READ_ID                      0x9f
#define ERASE_SECTOR								 0x20//擦除扇区代码
#define READ_STATUS								   0x05//0x05或者0x35
#define READ_DATA										 0x03//读数据命令
#define WRITE_DATA									 0x02//写数据命令
#define WRITE_ENABLE								 0x06//写使能命令
#define SEND_DATA_LENGTH						 256

void spi_flash_gpio_init(void);
void spi_flash_init(void);
void spi_waitfor_write_end(void);
uint8_t spi_flash_read_data(void);
uint32_t spi_flash_read_id(void);
void spi_erase_sector(uint32_t addr);
void spi_read_data(uint32_t addr, uint8_t *buffer,uint32_t numbytestoread);
void spi_write_data(uint32_t addr, uint8_t *write_buffer,uint32_t numbytestowrite);
void spi_write_enable(void);
#endif

spi_flash.c

#include "spi_flash.h"

//初始化SPI GPIO引脚

void spi_flash_gpio_init(void)
{
	GPIO_InitTypeDef  GPIO_InitStructure;
	//使能SPI相关时钟
	FLASH_SPI_GPIO_APBX_CLOCK(FLASH_SPI_GPIO_CLK, ENABLE);
  FLASH_SPI_APBX_CLOCK(FLASH_SPI_CLK, ENABLE);
	
	/**
	*	GPIO的所选用的模式要根据不同信号的开发板进行变动
	*/
  GPIO_InitStructure.GPIO_Pin = FLASH_SPI_SCK_PIN | FLASH_SPI_MOSI_PIN | FLASH_SPI_MISO_PIN;//F4系列全部配置推挽复用输出就行
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;//复用输出模式
  GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//推挽输出
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//50MHz
  GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉
  GPIO_Init(FLASH_SPI_SCK_PORT, &GPIO_InitStructure);//初始化GPIO
	
	//初始化CS引脚,使用软件控制,设置为推挽输出
	GPIO_InitStructure.GPIO_Pin = FLASH_SPI_CS_PIN;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;//普通输出模式
  GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//推挽输出
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;//50MHz
  GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉
  GPIO_Init(FLASH_SPI_CS_PORT, &GPIO_InitStructure);//初始化GPIO
	
	//这里一定要对引脚进行复用,才能连接上SPI1,否则读取flashID会返回0
	GPIO_PinAFConfig(GPIOB,GPIO_PinSource3,GPIO_AF_SPI1); //PB3复用为 SPI1
	GPIO_PinAFConfig(GPIOB,GPIO_PinSource4,GPIO_AF_SPI1); //PB4复用为 SPI1
	GPIO_PinAFConfig(GPIOB,GPIO_PinSource5,GPIO_AF_SPI1); //PB5复用为 SPI1
	
	//这里只针对SPI口初始化
	//RCC_APB2PeriphResetCmd(RCC_APB2Periph_SPI1,ENABLE);//复位SPI1
	//RCC_APB2PeriphResetCmd(RCC_APB2Periph_SPI1,DISABLE);//停止复位SPI1

	FLASH_SPI_CS_HIGH
}

//初始化SPI的结构体
void spi_flash_init(void)
{
	SPI_InitTypeDef SPI_InitStructure;
	SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_2;//配置波特率预分频值 
	SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;//配置为模式0
	SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;//配置CPHA和CPOL为四种模式之一即可
	SPI_InitStructure.SPI_CRCPolynomial = 0;//不适用CRC
	SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;//八位数据传输
	SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;//双线全双工
	SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;//高位有限发送
	SPI_InitStructure.SPI_Mode = SPI_Mode_Master;//作为主机
	SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;//使用软件控制cs
	//写入配置到寄存器
	SPI_Init(FLASH_SPIX,&SPI_InitStructure);
	//使能
	SPI_Cmd(FLASH_SPIX,ENABLE);
	
}

//发送并接收一个字节,接收也要发送数据 才能正常接收
uint8_t spi_flash_send_byte(uint8_t data)
{
	//检查TXE为空的时候再发送数据,缓冲区非空的标志为0
	while(SPI_I2S_GetFlagStatus(FLASH_SPIX,SPI_I2S_FLAG_TXE) == RESET);
	//通过这个while后说明数据BUFFER为空了,就可以发送数据了
	SPI_I2S_SendData(FLASH_SPIX,data);
	//检测RXNE标志为置1表示数据发送完毕
	while(SPI_I2S_GetFlagStatus(FLASH_SPIX,SPI_I2S_FLAG_RXNE) == RESET);
	//返回接收到的数据
	return SPI_I2S_ReceiveData(FLASH_SPIX);
}

uint8_t spi_flash_read_data(void)
{
	return spi_flash_send_byte(CHAN);//随意发送数据
}


//等待flash内部时序完成
void spi_waitfor_write_end(void)
{
	uint8_t status_flag = 0;
	//片选使能
	FLASH_SPI_CS_LOW;
	spi_flash_send_byte(READ_STATUS);
	do{
		//发送一个随机数据,来获取装态返回信息
		status_flag = spi_flash_send_byte(CHAN);
	}while((status_flag & 0x01) == 1 );
	FLASH_SPI_CS_HIGH;
}
//读取设备的id号
uint32_t spi_flash_read_id(void)
{
	uint32_t flash_id;//因为返回的到id是3个八位二进制位
	spi_write_enable();
	
	//片选使能
	FLASH_SPI_CS_LOW;
	spi_flash_send_byte(READ_ID);
	flash_id = spi_flash_send_byte(CHAN);
	flash_id <<= 8;
	flash_id |= spi_flash_send_byte(CHAN);
	flash_id <<= 8;
	flash_id |= spi_flash_send_byte(CHAN);
	
	FLASH_SPI_CS_HIGH;
	return flash_id;
}

//写使能命令
void spi_write_enable(void)
{
	FLASH_SPI_CS_LOW;
	
	spi_flash_send_byte(WRITE_ENABLE);
	
	FLASH_SPI_CS_HIGH;
}

//擦除指定扇区,参数位扇区地址
void spi_erase_sector(uint32_t addr)
{
	spi_write_enable();
	//片选使能
	FLASH_SPI_CS_LOW;
	spi_flash_send_byte(ERASE_SECTOR);
	spi_flash_send_byte((addr>>16)&0x0f);
	spi_flash_send_byte((addr>>8)&0x0f);
	spi_flash_send_byte(addr&0x0f);
	
	FLASH_SPI_CS_HIGH;
	//这里擦除数据一定要等待一下 不然会擦除不成功,导致后面的写入失败
	spi_waitfor_write_end();
}


//读取flash的内容
void spi_read_data(uint32_t addr, uint8_t *buffer,uint32_t numbytestoread)
{
	spi_write_enable();
	spi_waitfor_write_end();
	//片选使能
	FLASH_SPI_CS_LOW;
	spi_flash_send_byte(READ_DATA);//发送读命令
	spi_flash_send_byte((addr>>16)&0x0f);//将要读的24位地址从高到低发送
	spi_flash_send_byte((addr>>8)&0x0f);
	spi_flash_send_byte(addr&0x0f);
	
	while(numbytestoread --)
	{
		*buffer = spi_flash_send_byte(CHAN);
		buffer++;
	}
	FLASH_SPI_CS_HIGH;
	spi_waitfor_write_end();
}


//向flash进行写入数据
void spi_write_data(uint32_t addr, uint8_t *write_buffer,uint32_t numbytestowrite)
{
	//写数据之前要使能写命令
	spi_write_enable();
	spi_waitfor_write_end();
	//片选使能
	FLASH_SPI_CS_LOW;
	spi_flash_send_byte(WRITE_DATA);//发送写命令
	spi_flash_send_byte((uint8_t)((addr>>16)&0x0f));//将要读的24位地址从高到低发送
	spi_flash_send_byte((uint8_t)((addr>>8)&0x0f));
	spi_flash_send_byte((uint8_t)(addr&0x0f));
	
	while(numbytestowrite --)
	{
		spi_flash_send_byte(*write_buffer);
		write_buffer++;
	}
	FLASH_SPI_CS_HIGH;
	spi_waitfor_write_end();
}

在测试程序的时候加了个按键操作,可以自行修改主程序
key.h

#ifndef __KEY_H
#define __KEY_H	 
#include "sys.h" 

/*下面的方式是通过直接操作库函数方式读取IO*/
#define KEY0 		GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_4) //PE4
#define KEY1 		GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_3)	//PE3 
#define WK_UP 	GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_0)	//PA0


/*下面方式是通过位带操作方式读取IO*/
/*
#define KEY0 		PEin(4)   	//PE4
#define KEY1 		PEin(3)		//PE3 
#define KEY2 		PEin(2)		//P32
#define WK_UP 	PAin(0)		//PA0
*/


#define KEY0_PRES 	1
#define KEY1_PRES	2
//#define KEY2_PRES	3
#define WKUP_PRES   4

void KEY_Init(void);	//IO初始化
u8 KEY_Scan(u8);  		//按键扫描函数	

#endif

key.c

#include "key.h"
#include "delay.h" 

//按键初始化函数
void KEY_Init(void)
{
	
	GPIO_InitTypeDef  GPIO_InitStructure;

  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA|RCC_AHB1Periph_GPIOE, ENABLE);//使能GPIOA,GPIOE时钟
 
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3|GPIO_Pin_4; //KEY2 KEY3对应引脚
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;//普通输入模式
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100M
  GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉
  GPIO_Init(GPIOE, &GPIO_InitStructure);//初始化GPIOE2,3,4
	
	 
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;//WK_UP对应引脚PA0
  GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_DOWN ;//下拉
  GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA0
 
} 
//按键处理函数
//返回按键值
//mode:0,不支持连续按;1,支持连续按;
//0,没有任何按键按下
//1,KEY0按下
//2,KEY1按下
//3,KEY2按下 
//4,WKUP按下 WK_UP
//注意此函数有响应优先级,KEY0>KEY1>KEY2>WK_UP!!
u8 KEY_Scan(u8 mode)
{	 
	static u8 key_up=1;//按键按松开标志
	if(mode)key_up=1;  //支持连按		  
	if(key_up&&(KEY0==0||KEY1==0||WK_UP==1))
	{
		delay_ms(10);//去抖动 
		key_up=0;
		if(KEY0==0)return 1;
		else if(KEY1==0)return 2;
//		else if(KEY2==0)return 3;
		else if(WK_UP==1)return 4;
	}else if(KEY0==1&&KEY1==1&&WK_UP==0)key_up=1; 	    
 	return 0;// 无按键按下
}

mian.c文章来源地址https://www.toymoban.com/news/detail-610842.html

#include "sys.h"
#include "delay.h"
#include "usart.h"
#include "led.h"
#include "spi_flash.h"
#include "key.h"


//定义为全局变量,就不会占用栈的存储空间,因为4096超过限制,会导致程序卡死
uint8_t read_buffer[4096];
uint8_t write_buffer[4096];

int main(void)
{ 
	uint32_t chan_id;
	uint8_t key_status;
	int i;
	delay_init(168);		  //初始化延时函数
	LED_Init();		        //初始化LED端口
	KEY_Init();
	
	uart_init(115200);//初始化串口1
	printf("\r\n测试SPI读写FLASH功能\r\n");
	
	spi_flash_gpio_init();
	spi_flash_init();
	chan_id = spi_flash_read_id();
	printf("\r\n收到的chen_ID:%x\r\n",chan_id);
	//擦除数据
	spi_erase_sector(0);
	//等待擦除成功,等待步骤在擦除数据操作中已经写入,下面可以不写
	spi_waitfor_write_end();
	//写入数据
	for(int i = 0 ;i<SEND_DATA_LENGTH ;i++)
	{
		write_buffer[i] = i;
	}
	spi_write_data(0,write_buffer,SEND_DATA_LENGTH);
	spi_waitfor_write_end();//其实再write中已经写入了等待,可以不加
	
	

	while(1)
	{
		key_status = KEY_Scan(0);
		if(key_status)
		{
			GPIO_ResetBits(GPIOA,GPIO_Pin_6);
			spi_read_data(0,read_buffer,SEND_DATA_LENGTH);
			for(i = 0;i<SEND_DATA_LENGTH;i++)
			{
				printf(" 0x%x ",read_buffer[i]);
				if((i != 0) && (i%10 == 0))
				{
					printf("\r\n");
				}
			}
		}
//	GPIO_ResetBits(GPIOA,GPIO_Pin_6);  //LED0对应引脚GPIOA.6拉低,亮  等同LED0=0;
//	GPIO_SetBits(GPIOA,GPIO_Pin_7);   //LED1对应引脚GPIOA.7拉高,灭 等同LED1=1;
//	delay_ms(500);  		   //延时500ms
//	GPIO_SetBits(GPIOA,GPIO_Pin_6);	   //LED0对应引脚GPIOA.6拉高,灭  等同LED0=1;
//	GPIO_ResetBits(GPIOA,GPIO_Pin_7); //LED1对应引脚GPIOA.7拉低,亮 等同LED1=0;
//	delay_ms(500);                     //延时300ms
	}
}

到了这里,关于STM32F407VET6使用SPI访问Flash数据返回0xff(先写入数据,再读取数据,却返回0xff,但是通过debug却可以正常输出)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • STM32F407 SPI配置和时序图讲解(二)

    上节讲了SPI的基本配置,这节主要讲解 如何看时序图 ,SPI数据到底是如何传输的。 SPI初始化后,就可以开始向对象发送数据了,但是要发送数据给W25Q128模块,需要按照它的时序图来发送( 个人用的是W25Q128模块 ) W25Q128模块简介 W25Q128是一款常见的串行闪存存储器模块,属

    2024年02月06日
    浏览(18)
  • 【正点原子STM32连载】 第四十三章 SPI实验 摘自【正点原子】APM32F407最小系统板使用指南

    1)实验平台:正点原子stm32f103战舰开发板V4 2)平台购买地址:https://detail.tmall.com/item.htm?id=609294757420 3)全套实验源码+手册+视频下载地址: http://www.openedv.com/thread-340252-1-1.html## 本章将介绍使用APM32F407驱动板载的NOR Flash进行读写操作。通过本章的学习,读者将学习到使用SPI驱

    2024年02月08日
    浏览(21)
  • 【STM32】BLDC驱动&控制开发笔记 | 07_SPI通信测试 - STM32F407用SPI配置DRV8323驱动芯片

    最近在埋头搞STM32 + 无刷直流电机控制,想实现用自己的STM32F407VGT6芯片板子,外加一块驱动板(目前选用到TI的DRV8302或者DRV8323驱动芯片),搞定电机驱动,最后实现比较好的控制效果。如果不是同一块芯片的同学也不用急着走,大体上都是可借鉴哒~ 本文主要实现使用SPI通信

    2024年02月08日
    浏览(28)
  • STM32的入门——CM3芯片STM32F103VET6的使用和相关外设

    根据《STM32库开发实战指南——基于野火指南者开发板》整理, https://gitee.com/Embedfire-stm32f103-zhinanzhe/ebf_stm32f103_zhinanzhe_ std_tutorial 在整理的过程中,还参考了其他的一些STM32资料 使用了“幕布”这款软件,添加了一些思维导图 整理框架目的: 1.梳理知识脉络 2.复习知识 被动单

    2024年02月03日
    浏览(21)
  • 【DRV8323】电机驱动芯片寄存器配置指南,通过STM32F407的SPI通信配置

    笔者计划使用一块使用到STM32F407控制芯片与DRV8323s驱动芯片的板子,驱动BLDC。了解到需要使用SPI通信来配置DRV8323s驱动芯片,配置过程中涉及DRV8323数据手册中提及的几个寄存器,故写此文做个记录。 另外,DRV8323芯片和DRV8302、DRV8303、DRV8353都有极大的相似之处,可以相互参考

    2024年02月02日
    浏览(36)
  • STM32F103xx / STM32F429VET6最小系统原理图

    STM32F429VET6核心板原理图 2023.12.09修改内容:打板后由于更换学习方向并未进行测试,所给原理图仅供参考,给出PDF下载链接,未设置积分和会员下载:https://download.csdn.net/download/m0_51294753/88611702。 一、前言 先前使用过的是STM32F1系列,只使用和绘制过STM32F103C8T6和STM32F103ZET6的板

    2023年04月24日
    浏览(18)
  • STM32G473VET6 FlashDB数据库移植(裸机、片内Flash)

    此文档也适用于STM32G070 此处使用FlashDB官方最新源码 FlashDB: 一款支持 KV 数据和时序数据的超轻量级数据库 (gitee.com) 克隆源码后目录如下 红框中几个为移植必要文件与参考 根据FlashDB官方文档可知,FlashDB底层依赖于RT-Thread的FAL组件,所以需要先移植FAL FlashDB源码中port目录下即

    2024年01月21日
    浏览(20)
  • STM32F407 --USART使用

    目录 1. 串口配置--普通模式 2. 实现数据的传输主函数 1)单引号双引号的应用数组传输 2)将调试信息用串口打印传送到电脑上 1. 串口配置--普通模式 F407使用的M4内核与F103使用的M3内核不一样,导致在使用配置上有区别。需要在F103配置的基础上专门将GPIO的PIN配置成复用功能

    2024年02月16日
    浏览(19)
  • stm32第一节:认识寄存器(野火指南者——STM32F103VET6)

            STM32芯片架构                 Cortex-M3内核(arm)——(I,S,D)——总线矩阵——外设, Flash,SRAM                 外设—— GPIO,USART,12C,SPI ……         Flash及SRAM储存                 Flash——常量                 SRAM——变量         DMA作用   

    2024年02月21日
    浏览(26)
  • stm32f103VET6和stm32f103c8t6有什么区别?

    我来终结下这个问题。 这两款单片机我都用过,其中无际单片机特训营其中一款wifi报警主机项目就是用了stm32f103c8t6。  stm32f103VET6和stm32f103c8t6都是STMicroelectronics公司推出基于ARM Cortex-M3内核的单片机。 它们在硬件规格和性能上存在一些差异,下面我详细讲解下它们的差异之处

    2024年02月15日
    浏览(21)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包