实验(三):SPI应用:读写串行FLASH 实验

这篇具有很好参考价值的文章主要介绍了实验(三):SPI应用:读写串行FLASH 实验。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

一、实验目的与任务

实验目的:

1. 学习对SPI的使用;

2. 掌握KEIL5的仿真与调试。

任务:

1. 根据要求编写程序,并写出原理性注释;

2. 将检查程序运行的结果,分析一下是否正确;

3. 完成所建工程的验证调试。

二、实验要求

以一种使用SPI 通讯的串行FLASH 存储芯片的读写实验为大家讲解STM32 的SPI 使用方法。实验中STM32 的SPI 外设采用主模式,通过查询事件的方式来确保正常通讯。

flash hold引脚,单片机,实验报告,单片机,stm32,嵌入式硬件,实验报告

三、实验内容及步骤

本实验板中的FLASH 芯片(型号:W25Q64)是一种使用SPI 通讯协议的NOR FLASH存储器, 它的CS/CLK/DIO/DO 引脚分别连接到了STM32 对应的SPI 引脚NSS/SCK/MOSI/MISO 上,其中STM32 的NSS 引脚是一个普通的GPIO,不是SPI 的专用NSS 引脚,所以程序中我们要使用软件控制的方式。

FLASH 芯片中还有WP 和HOLD 引脚。WP 引脚可控制写保护功能,当该引脚为低电平时,禁止写入数据。我们直接接电源,不使用写保护功能。HOLD 引脚可用于暂停通讯,该引脚为低电平时,通讯暂停,数据输出引脚输出高阻抗状态,时钟和数据输入引脚无效。我们直接接电源,不使用通讯暂停功能。

1. 软件设计

① 实验新建文件步骤:

运行Keil 5开发环境。首先编写两个SPI底层驱动文件,MySPI.c 和 MySPI.h,用来存放SPI通讯协议的驱动,接着编写三个W25Q64使用的功能函数文件,W25Q64.c、W25Q64.h、W25Q64_Ins.h,其中W25Q64.c文件存放使用的功能函数,W25Q64_Ins.h中存放指令码。

② 编程要点:

  • 初始化通讯使用的目标引脚及端口时钟;
  • 使能SPI 外设的时钟;
  • 配置SPI 外设的模式、地址、速率等参数并使能SPI 外设;
  • 编写基本SPI 按字节收发的函数;
  • 编写对FLASH擦除及读写操作的的函数;
  • 编写测试程序,对读写数据进行校验。

2. 实验步骤

(1)运行Keil uVision5开发环境,建立一个项目工程。

(2)在工程中添加main.c文件,因需要用到OLED显示屏,所以将之前实验写好的OLED文件移植到该工程中,然后在main.c中调用,如图1所示。

flash hold引脚,单片机,实验报告,单片机,stm32,嵌入式硬件,实验报告

图1 移植程序

(3)在工程中添加SPI底层驱动文件,因此需要创建MySPI.c文件,编写SPI通讯时序,如图2所示。

flash hold引脚,单片机,实验报告,单片机,stm32,嵌入式硬件,实验报告

图2 编写MySPI.c代码

 (4)编写MySPI.h程序,方便以后工程文件以移植,使项目工程工具有移植性,如图3所示。

flash hold引脚,单片机,实验报告,单片机,stm32,嵌入式硬件,实验报告

图3 MySPI.h程序

(5)在工程中添加W25Q64使用的功能函数文件,因此需要创建W25Q64_Ins.h文件,存放指令码,如图4所示。

flash hold引脚,单片机,实验报告,单片机,stm32,嵌入式硬件,实验报告

图4 编写W25Q64指令码

(6)创建W25Q64.c文件,编写需要使用的功能函数,如图5所示。

flash hold引脚,单片机,实验报告,单片机,stm32,嵌入式硬件,实验报告

图5 编写W25Q64功能函数

(7)编写W25Q64.h程序,方便以后工程文件以移植,使项目工程工具有移植性,如图6所示。

flash hold引脚,单片机,实验报告,单片机,stm32,嵌入式硬件,实验报告

图6 编写W25Q64.h程序

(8)编写main.c程序,读取设备ID,写入数据,读出数据,如图7所示。

flash hold引脚,单片机,实验报告,单片机,stm32,嵌入式硬件,实验报告

图7 main.c程序

运行并调试成功并无错误和警告。

3. 调试验证及结果

(1)将开发板连接到电脑上,使用跳线在面包板上将W25Q64和单片机连接,使用STLINK将程序烧录到STM32中,如图8所示。

flash hold引脚,单片机,实验报告,单片机,stm32,嵌入式硬件,实验报告

图8 线路连接

(2)程序烧录后,实验现象如图9所示:

flash hold引脚,单片机,实验报告,单片机,stm32,嵌入式硬件,实验报告

图9 实验现象

四、实验代码分析

(1)MySPI.c:

#include "MySPI.h"

/**
  * @brief  引脚封装
  * @param  
  * @retval 
  */
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);
}

uint8_t MySPI_R_MISO(void)
{
	return GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_6);
}

/**
  * @brief  SPI初始化
  * @param  
  * @retval 
  */
void MySPI_Init(void)
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
	
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_7;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	
	MySPI_W_SS(1);//SS置高电平,不选中从机
	MySPI_W_SCK(0);//SPI模式0
}

/**
  * @brief  起始信号
  * @param  SS置低电平 
  * @retval 
  */
void MySPI_Start(void)
{
	MySPI_W_SS(0);
}

/**
  * @brief  终止信号
  * @param  SS置高电平
  * @retval 
  */
void MySPI_Stop(void)
{
	MySPI_W_SS(1);
}

/**
  * @brief  交互数据     模式0   SS下降沿->移出数据->SCK上升沿->移入数据->SCK下降沿->移出数据
  * @param  ByteSend     需要发送出去的数据
  * @retval ByteReceive  需要接收到的数据
  */
uint8_t MySPI_SwapByte(uint8_t ByteSend)
{
	uint8_t i, ByteReceive = 0x00;
	
	for (i = 0; i < 8; i ++)
	{
		MySPI_W_MOSI(ByteSend & (0x80 >> i));
		MySPI_W_SCK(1);
		if (MySPI_R_MISO() == 1){ByteReceive |= (0x80 >> i);}
		MySPI_W_SCK(0);
	}
	
	return ByteReceive;
}
/**
*模式2,在模式0的基础上,把出现SCK的地方,1改0,0改1
*/


/**
  * @brief  交互数据     模式1
  * @param  ByteSend     需要发送出去的数据
  * @retval ByteReceive  需要接收到的数据
  */
uint8_t MySPI_SwapByte1(uint8_t ByteSend)
{
	uint8_t i, ByteReceive = 0x00;
	
	for (i = 0; i < 8; i ++)
	{
		MySPI_W_SCK(1);
		MySPI_W_MOSI(ByteSend & (0x80 >> i));
		MySPI_W_SCK(0);
		if (MySPI_R_MISO() == 1){ByteReceive |= (0x80 >> i);}

	}
	
	return ByteReceive;
}
/**
*模式3,在模式1的基础上,把出现SCK的地方,1改0,0改1
*/

(2)W25Q64.c:

#include "W25Q64.h"

//初始化
void W25Q64_Init(void)
{
	MySPI_Init();
}
/**
  * @brief  获取ID号
  * @param  MID厂商ID  DID设备ID
  * @retval 
  */
void W25Q64_ReadID(uint8_t *MID, uint16_t *DID)
{
	MySPI_Start();
	
	MySPI_SwapByte(W25Q64_JEDEC_ID);//读ID号指令发送
	
	*MID = MySPI_SwapByte(W25Q64_DUMMY_BYTE);//厂商ID 
	
	*DID = MySPI_SwapByte(W25Q64_DUMMY_BYTE);//设备ID高八位
	*DID <<= 8;
	*DID |= MySPI_SwapByte(W25Q64_DUMMY_BYTE);//设备ID低八位
	
	MySPI_Stop();
}

/**
  * @brief  写使能
  * @param  
  * @retval 
  */
void W25Q64_WriteEnable(void)
{
	MySPI_Start();
	MySPI_SwapByte(W25Q64_WRITE_ENABLE);//发送指令
	MySPI_Stop();
}

/**
  * @brief  查看芯片是否处于忙状态
  * @param  
  * @retval 
  */
void W25Q64_WaitBusy(void)
{
	uint32_t Timeout;
	
	MySPI_Start();
	MySPI_SwapByte(W25Q64_READ_STATUS_REGISTER_1);//发送读状态寄存器1指令
	Timeout = 100000;
	while ((MySPI_SwapByte(W25Q64_DUMMY_BYTE) & 0x01) == 0x01)//获取最低位进行查忙判断
	{
		//避免死循环,程序卡死
		Timeout --;
		if (Timeout == 0)
		{
			break;
		}
	}
	MySPI_Stop();
}

/**
  * @brief  页编程
  * @param  Address24位地址  DataArray  Count
  * @retval 
  */
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();
}

/**
* @brief  扇区擦除
  * @param  
  * @retval 
  */
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();
}

/**
  * @brief  读数据
  * @param  
  * @retval 
  */
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();
}

(3)W25Q64_Ins.h:

#ifndef __W25Q64_INS_H
#define __W25Q64_INS_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

#endif

(4)main函数程序:

#include "stm32f10x.h"                  // Device header
#include "Delay.h" 
#include "OLED.h"
#include "W25Q64.h"

uint8_t MID;
uint16_t DID;

uint8_t ArrayWrite[] = {0xAA, 0x02, 0xFE, 0x05};
uint8_t ArrayRead[4];

int main(void)
{
	OLED_Init();
	W25Q64_Init();
	
	OLED_ShowString(1, 1, "MID:   DID:");
	OLED_ShowString(2, 1, "W:");
	OLED_ShowString(3, 1, "R:");
	
	W25Q64_ReadID(&MID, &DID);
	OLED_ShowHexNum(1, 5, MID, 2);
	OLED_ShowHexNum(1, 12, DID, 4);
	
	W25Q64_SectorErase(0x000000);
	
	W25Q64_PageProgram(0x000000, ArrayWrite, 4);
	
	W25Q64_ReadData(0x000000, ArrayRead, 4);
	
	OLED_ShowHexNum(2, 3, ArrayWrite[0], 2);
	OLED_ShowHexNum(2, 6, ArrayWrite[1], 2);
	OLED_ShowHexNum(2, 9, ArrayWrite[2], 2);
	OLED_ShowHexNum(2, 12, ArrayWrite[3], 2);
	
	OLED_ShowHexNum(3, 3, ArrayRead[0], 2);
	OLED_ShowHexNum(3, 6, ArrayRead[1], 2);
	OLED_ShowHexNum(3, 9, ArrayRead[2], 2);
	OLED_ShowHexNum(3, 12, ArrayRead[3], 2);
	
	while (1)
	{
		
	}
}

五、实验总结

本次实验的主要目的是学习并掌握SPI(串行外围接口)的使用,以及使用KEIL5的仿真与调试工具进行验证。通过实验,我对SPI的原理和使用方法有了更深入的理解,并且掌握了在STM32中配置和操作SPI外设的技巧。以下是我对本次实验的总结和心得体会:

在本次实验中,我首先了解了SPI的基本原理。SPI是一种串行通信协议,通过四根信号线进行通信,包括时钟信号线(SCLK)、主设备输出从设备输入的数据线(MOSI)、主设备输入从设备输出的数据线(MISO)和片选信号线(SS)。SPI支持全双工通信,可以同时进行数据发送和接收。了解了SPI的基本原理之后,我明白了如何在STM32中配置和使用SPI外设。

在编写程序时,我按照实验要求,首先进行SPI外设的初始化,确保其处于就绪状态。然后,我根据目标设备的通信协议,设置正确的片选信号,以选择要与之通信的设备。接下来,我使用HAL库提供的函数进行数据的发送和接收,确保数据在SPI总线上的正确传输。在编写过程中,我注重添加适当的注释,以便于代码的理解和维护。

在验证和调试阶段,我使用了KEIL5的仿真与调试工具。通过设置断点、监视变量的值,我可以逐步执行程序,观察代码的运行情况。在调试过程中,我发现一些潜在的问题,并进行了修复和调试。调试工具为我提供了一个实时的、可视化的方式来观察代码的执行情况,提高了调试的效率。

通过本次实验,我不仅学会了配置和使用SPI外设,还加深了对SPI原理的理解。我意识到SPI是一种高速的串行通信协议,适用于与外部设备进行数据交换,如存储芯片、传感器等。SPI的全双工特性使得数据的发送和接收可以同时进行,提高了数据传输的效率。同时,我也意识到在实际应用中,需要根据不同的设备和通信要求,灵活配置SPI的参数。

总的来说,本次实验使我对SPI的应用有了更深入的了解。通过实际操作和调试,我掌握了SPI的配置和使用方法,并提高了对SPI外设的理解能力。这对我今后的嵌入式系统开发和硬件通信方面的工作都具有重要的意义。我将继续深入学习和探索SPI的更高级特性和应用场景,以提升自己在嵌入式系统开发领域的能力。

源码:实验3 文章来源地址https://www.toymoban.com/news/detail-812307.html

到了这里,关于实验(三):SPI应用:读写串行FLASH 实验的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

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

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

相关文章

  • FPGA模块——SPI协议(读写FLASH)

    芯片引脚图: 内部结构图: 存储区域总共分成了32块,每块64KB。每块又分成了16个部分,每个部分4KB。方便进行读取和局部操作。 电路设计 SPI的四种模式 这里使用这个模式: 主机和从机在时钟上升沿放入要输出的数据,在时钟下降沿读取要输入的数据。 8个时钟后交换一个

    2024年02月05日
    浏览(36)
  • AXI Quad SPI读写Flash做远程升级

    未经允许,本文禁止转载 目录 简介 AXI Quad SPI IP设置 寄存器说明 AXI Quad SPI支持的通用命令 读flash id 读flash 数据 擦除扇区 写flash 数据 注意事项         本文简要介绍xilinx 7系的AXI quad spi IP核的使用,主要用于读写boot用的flash(n25q128为例)做在线升级用。本文会略去很多细节,

    2024年02月03日
    浏览(31)
  • 09_SPI-Flash 页写实验

    使用页写指令,向 Flash 中写入 N 字节数据,N 为整数,且大于 0 小于等于 256。在本 实 验 中 我 们 向 Flash 芯 片 中 写 入 0-99 , 共 100 字 节 数 据 , 数 据 初 始 地 址 为24’h00_04_25。 注意:在向 Flash 芯片写入数据之前,先要对芯片执行全擦除操作。 写满不支持跨页写,在这

    2024年02月16日
    浏览(22)
  • 【STM32】SPI初步使用 读写FLASH W25Q64

    (1) SS( Slave Select):从设备选择信号线,常称为片选信号线,每个从设备都有独立的这一条 NSS 信号线,当主机要选择从设备时,把该从设备的 NSS 信号线设置为低电平,该从设备即被选中,即片选有效,接着主机开始与被选中的从设备进行 SPI通讯。所以 SPI通讯以 NSS 线置低电

    2024年02月10日
    浏览(38)
  • 08_SPI-Flash 扇区擦除实验

    编写扇区擦除工程,擦除事先烧录到 Flash 中的流水灯程序所占的某个扇区,使流水灯程序不能正常工作。在此次实验工程,我们选择擦除第 0 个扇区,擦除地址为24’h00_04_25。 扇区擦除(Sector Erase)操作,简称 SE,操作指令为 8’b1101_0000(D8h)。

    2024年02月16日
    浏览(25)
  • 【FLASH存储器系列五】SPI NOR FLASH芯片使用指导之一

    👉个人主页: highman110 👉作者简介:一名硬件工程师,持续学习,不断记录,保持思考,输出干货内容   目录 1芯片简介 2引脚定义 3功能框图 4器件操作 4.1操作框图 4.2标准SPI 4.3DaulSPI 4.4QaudSPI 4.5QPI 4.6DTR(W25Q128不支持) 4.73-字节/4-字节地址模式(W25Q128只支持3字节) 4.8保持

    2023年04月19日
    浏览(35)
  • SPI FLASH扇区擦除

    目录 一、扇区擦除  Sector Erase  指令 (20h)          1、步骤                 a、扇区擦除前,必须解锁FLASH,也就是写使能       (06h)                 b、FLASH进行扇区擦除,看第一个图                         (20h)                 c、检查是否擦除 状

    2024年02月09日
    浏览(25)
  • SPI 及 NOR Flash 介绍

    1.SPI的含义 SPI:串行外设设备接口(Serial Peripheral Interface),是一种高速的,全双工,同步的通信总线。SPI接口主要应用在存储芯片、AD转换器以及LCD中。SPI接口主要应用在存储芯片、AD转换器以及LCD中。 SPI 的引脚信息: MISO(Master In / Slave Out)主设备数据输入,从设备数据

    2024年02月12日
    浏览(27)
  • FPGA使用SPI控制FLASH

    通过控制FLASH芯片进一步熟悉SPI协议 Flash 存储器 : Flash 存储器是一种非易失性存储器,它具有 RAM 和 ROM 的一些特点。与 ROM 类似,Flash 存储器的内容在断电时不会丢失,但与 RAM 类似,它可以通过编程来修改存储的内容。Flash 存储器通常用于嵌入式系统中存储程序代码、配置

    2024年03月19日
    浏览(40)
  • SPI FLASH Fatfs文件系统移植

    FATFS是面向小型嵌入式系统的FAT文件系统。他由C语言编写并且独立与底层I/O介质。支持的内核有:8051,PLC,ARV,ARM等。FATFS支持FAT12,FAT16,FAT32等文件系统格式。 官网链接 diskio.c:包含底层存储介质的操作函数,需要与硬件设备适配移植。主要是在这个文件里调用用户实现的底层驱

    2024年02月08日
    浏览(29)

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

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

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

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

二维码1

领取红包

二维码2

领红包