NRF51822——FLASH驱动

本文详细介绍了SPI驱动Flash(25Q16)的初始化过程及各种读写操作,包括GPIO配置、状态寄存器读写、页编程、快速读取、擦除扇区、块和整个芯片的功能实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言:

为了方便查看博客,特意申请了一个公众号,附上二维码,有兴趣的朋友可以关注,和我一起讨论学习,一起享受技术,一起成长。

在这里插入图片描述


1. 简述

SPI是串行外设通信接口,主要实现主从设备之间的通信。硬件上由CS、SCK、MISO、MOSI四根通信线连接而成。

硬件连接如下:

这里写图片描述

2.软件实现

//头文件
#ifndef __FLASH__H__
#define __FLASH__H__

#include "nrf51.h"

#define  FLASH_WRITE_ENABLE_CMD 		0x06
#define  FLASH_WRITE_DISABLE_CMD		0x04
#define  FLASH_READ_SR_CMD				0x05
#define  FLASH_WRITE_SR_CMD				0x01
#define  FLASH_READ_DATA				0x03
#define  FLASH_FASTREAD_DATA			0x0b
#define  FLASH_WRITE_PAGE				0x02
#define  FLASH_ERASE_SECTOR       		0x20
#define	 FLASH_ERASE_BLOCK				0xd8
#define	 FLASH_ERASE_CHIP				0xc7
#define  FLASH_POWER_DOWN				0xb9
#define  FLASH_RELEASE_POWER_DOWN       0xab
#define  FLASH_READ_DEVICE_ID      		0x90
#define  FLASH_READ_JEDEC_ID      		0x9f

#define 	FLASH_SIZE	 (2*1024*1024)	// 2M字节
#define		PAGE_SIZE			8192	// 256 bytes
#define 	SECTOR_SIZE		512	 // 4-Kbyte
#define		BLOCK_SIZE		32	// 64-Kbyte	

#define PAGE_LEN		255	 //一页256字节

//定义FLASH相关属性定义
struct Flash_Attr  {
	uint16_t flash_id;
	uint16_t page_size;
	uint16_t sector_size;
	uint8_t block_size;
};
//#if 0
//#define FLASH_CS 				29
//else
#define FLASH_CS 				30


#define FLASH_CS_LOW			nrf_gpio_pin_clear(FLASH_CS)
#define FLASH_CS_HIGH 			nrf_gpio_pin_set(FLASH_CS)

void Flash_Gpio_Init(void);
void Flash_Write_Enable(void);
void Flash_Write_Disable(void);
uint8_t Flash_Read_SR(void);
void Flash_Write_SR(uint8_t dat);
void Flash_Wait_Nobusy(void);
void Flash_Read_Byte(uint8_t *pBuffer,uint32_t ReadAddr,uint16_t nByte);
void Flash_FastRead_Byte(uint8_t *pBuffer,uint32_t ReadAddr,uint16_t nByte);
void Flash_Write_Page(uint8_t *pBuffer,uint32_t WriteAddr,uint16_t nByte);
void Flash_Write_NoCheck(uint8_t *pBuffer,uint32_t WriteAddr,uint16_t nByte);
void Flash_Write_Byte(uint8_t *pBuffer,uint32_t WriteAddr,uint16_t nByte);
void Flash_Erase_Sector(uint32_t SectorAddr);
void Flash_Erase_Block(uint32_t BlockAddr);
void Flash_Erase_Chip(void);
void Flash_Power_Down(void);
void Flash_Wake_Up(void);
uint16_t Flash_Read_Device_ID(void);
uint32_t Flash_Read_JEDEC_ID(void);


#endif
/******************************************************
说明:SPI驱动Flash(25Q16)
W25X16芯片容量:2MB (16Mbit)
          页数:16*16*32 (2M/256)
        扇区数:16*32
          块数:32

2、读写操作:
读 ------------ 一次最大读一页(256B)
写 ------------ 页
擦出 ---------- 扇区、块、整个芯片

3、控制和状态寄存器命令(默认:0x00)
BIT位  7   6   5   4   3   2   1   0
      SPR  RV  TB  BP2 BP1 BP0 WEL BUSY
SPR:默认0,状态寄存器保护位,配合WP使用
TB,BP2,BP1,BP0:FLASH区域写保护设置
WEL:写使能锁定
BUSY:忙标记位(1,忙;0,空闲)
******************************************************/

#include "nrf51.h"
#include "nrf_gpio.h"
#include "flash.h"
#include "spi.h"

struct Flash_Attr flash_pt;

void Flash_Gpio_Init(void)
{
	nrf_gpio_cfg_output(FLASH_CS);	//片选
	FLASH_CS_HIGH;
}

void Flash_Write_Enable(void)
{
	FLASH_CS_LOW;
	Spi_Write_Byte(FLASH_WRITE_ENABLE_CMD);//开启写使能
	FLASH_CS_HIGH;
}

void Flash_Write_Disable(void)
{
	FLASH_CS_LOW;
	Spi_Write_Byte(FLASH_WRITE_DISABLE_CMD);//开启写失能
	FLASH_CS_HIGH;
}

//读状态寄存器
uint8_t Flash_Read_SR(void)
{
	uint8_t dat;
	
	FLASH_CS_LOW;
	Spi_Write_Byte(FLASH_READ_SR_CMD);
	dat = Spi_Read_Byte();
	FLASH_CS_HIGH;
	
	return dat;
}

//写状态寄存器
void Flash_Write_SR(uint8_t dat)
{
	Flash_Write_Enable();
	Spi_Write_Byte(FLASH_WRITE_SR_CMD);
	Spi_Write_Byte(dat);//写入一个字节
	FLASH_CS_HIGH;
}

//判断状态寄存器的R0位:执行结束操作清零
void Flash_Wait_Nobusy(void)
{
	while(((Flash_Read_SR()) & 0x01)==0x01);//等待BUSY位清空
}

/*********************************************************************
* 读取数据:ReadAddr开始的地址,连续读出nByte长度的字节
* pBuffer ---- 数据存储区首地址
* ReadAddr --- 要读取SFLASH Flash的首地址地址
* nByte ------ 要读取的字节数(最大65535B = 64K 块)
**********************************************************************/
void Flash_Read_Byte(uint8_t *pBuffer,uint32_t ReadAddr,uint16_t nByte)
{
	FLASH_CS_LOW;
	Spi_Write_Byte(FLASH_READ_DATA);//连续读取数据
	Spi_Write_Byte((uint8_t)(ReadAddr>>16));//写入24位地址
	Spi_Write_Byte((uint8_t)(ReadAddr>>8));
	Spi_Write_Byte((uint8_t)(ReadAddr));
	while(nByte--)
	{
		*pBuffer = Spi_Read_Byte();
		pBuffer++;
	}
	FLASH_CS_HIGH;
}

//快速读取数据
void Flash_FastRead_Byte(uint8_t *pBuffer,uint32_t ReadAddr,uint16_t nByte)
{
	FLASH_CS_LOW;
	Spi_Write_Byte(FLASH_FASTREAD_DATA);//快速读取数据
	Spi_Write_Byte((uint8_t)(ReadAddr>>16));//写入24位地址
	Spi_Write_Byte((uint8_t)(ReadAddr>>8));
	Spi_Write_Byte((uint8_t)(ReadAddr));
	Spi_Write_Byte(0xff);//等待8个时钟
	while(nByte--)
	{
		*pBuffer = Spi_Read_Byte();
		pBuffer++;
	}
	FLASH_CS_HIGH;
}

//页编程:256 Bytes x 8192
/************************************************
函数名称 : Flash_Write_Page
功    能 : 在SFLASH内写入少于1页(256个字节)的数据
参    数 : pBuffer ----- 写入数据区首地址
            WriteAddr --- 要写入Flash的地址
            nByte ------- 要写入的字节数(最大1页)
返 回 值 : 无
作    者 : strongerHuang
*************************************************/
void Flash_Write_Page(uint8_t *pBuffer,uint32_t WriteAddr,uint16_t nByte)
{
	FLASH_CS_LOW;
	Spi_Write_Byte(FLASH_WRITE_PAGE);//页编程指令
	Spi_Write_Byte((uint8_t)(WriteAddr>>16));//写入24位地址
	Spi_Write_Byte((uint8_t)(WriteAddr>>8));
	Spi_Write_Byte((uint8_t)(WriteAddr));
	while(nByte--)
	{
		Spi_Write_Byte(*pBuffer);
		pBuffer++;
	}
	FLASH_CS_HIGH;
	Flash_Wait_Nobusy();//等待写入结束
}

//无检验写入
//无检验写SPI FLASH 
//必须确保所写的地址范围内的数据全部为0XFF,否则在非0XFF处写入的数据将失败!
//具有自动换页功能 
//在指定地址开始写入指定长度的数据,但是要确保地址不越界!
//pBuffer:数据存储区
//WriteAddr:开始写入的地址(24bit)
//nByte:要写入的字节数(最大65535)
//CHECK OK
void Flash_Write_NoCheck(uint8_t *pBuffer,uint32_t WriteAddr,uint16_t nByte)
{
	uint16_t PageByte = 256 - WriteAddr % 256;//单页剩余可写字节数

	if(nByte <= PageByte)	//不大于256字节
	{
		PageByte = nByte;
	}
	while(1)
	{
		Flash_Write_Page(pBuffer,WriteAddr,PageByte);
		if(nByte == PageByte)	//写入结束
			break;
		else
		{
			pBuffer += PageByte;//下一页写入的数据
			WriteAddr += PageByte;//下一页写入的地址
			nByte -= PageByte;//待写入的字节数递减
			if(nByte > 256)
			{
				PageByte = 256;
			}
			else
			{
				PageByte = nByte;
			}
		}
	}
}

/*********************************************************************
* 写入数据:WriteAddr开始的地址,连续写入nByte长度的字节
* pBuffer ---- 数据存储区首地址
* WriteAddr --- 要读取SFLASH Flash的首地址地址
* nByte ------ 要写入的字节数(最大65535B = 64K 块)
**********************************************************************/
void Flash_Write_Byte(uint8_t *pBuffer,uint32_t WriteAddr,uint16_t nByte)
{
	static uint8_t SectorBuf[4096];//扇区buf
	uint32_t SecPos;				//得到扇区位置
	uint16_t SecOff;				//扇区偏移
	uint16_t SecRemain;		//剩余扇区
	uint16_t i;

	SecPos = WriteAddr / 4096;//地址所在扇区(0--511)
	SecOff = WriteAddr % 4096;//扇区内地址偏移
	SecRemain = 4096 - SecOff;//扇区除去偏移,还剩多少字节

	if(nByte <= SecRemain)	//写入数据大小 < 剩余扇区空间大小
	{
		SecRemain = nByte;
	}

	while(1)
	{
		Flash_Read_Byte(SectorBuf, SecPos*4096, 4096);//读出整个扇区的内容
		for(i=0;i<SecRemain;i++)	//校验数据
		{
			if(SectorBuf[SecOff + i] != 0xff)//储存数据不为0xff,需要擦除
			break;
		}
		if( i< SecRemain)	//需要擦除
		{
			Flash_Erase_Sector(SecPos);	//擦除这个扇区
			for(i=0;i<SecRemain;i++)	//保存写入的数据
			{
				SectorBuf[SecOff + i] = pBuffer[i];
			}
			Flash_Write_NoCheck(SectorBuf, SecPos*4096, 4096);//写入整个扇区(扇区=老数据+新写入数据)
		}
		else
		{
			Flash_Write_NoCheck(pBuffer,WriteAddr,SecRemain);//不需要擦除,直接写入扇区
		}
		if(nByte == SecRemain)	//写入结束
		{
			Flash_Write_Disable();
			break;
		}
		else
		{
			SecPos++;		//扇区地址增加1
			SecOff = 0;		//扇区偏移归零
			pBuffer += SecRemain;	//指针偏移
			WriteAddr += SecRemain;	//写地址偏移
			nByte -= SecRemain;	//待写入的字节递减
			if(nByte > 4096)
			{
				SecRemain = 4096;	//待写入一扇区(4096字节大小)
			}
			else
			{
				SecRemain = nByte;		//待写入少于一扇区的数据
			}
		}
		
	}
	
}

//擦除扇区:4K Bytes x 512
void Flash_Erase_Sector(uint32_t SectorAddr)
{
	SectorAddr *= 4096;
	Flash_Write_Enable();
	Flash_Wait_Nobusy();
	FLASH_CS_LOW;
	Spi_Write_Byte(FLASH_ERASE_SECTOR);//页编程指令
	Spi_Write_Byte((uint8_t)(SectorAddr>>16));//写入24位地址
	Spi_Write_Byte((uint8_t)(SectorAddr>>8));
	Spi_Write_Byte((uint8_t)(SectorAddr));
	FLASH_CS_HIGH;
	Flash_Wait_Nobusy();//等待写入结束
}

//擦除块:64K Bytes x 32
void Flash_Erase_Block(uint32_t BlockAddr)
{
	BlockAddr *= 65536;
	Flash_Write_Enable();
	Flash_Wait_Nobusy();
	FLASH_CS_LOW;
	Spi_Write_Byte(FLASH_ERASE_BLOCK);//块擦除
	Spi_Write_Byte((uint8_t)(BlockAddr>>16));//写入24位地址
	Spi_Write_Byte((uint8_t)(BlockAddr>>8));
	Spi_Write_Byte((uint8_t)(BlockAddr));
	FLASH_CS_HIGH;
	Flash_Wait_Nobusy();//等待写入结束
}

//整个芯片擦除
void Flash_Erase_Chip(void)
{
	Flash_Write_Enable();
	Flash_Wait_Nobusy();
	FLASH_CS_LOW;
	Spi_Write_Byte(FLASH_ERASE_CHIP);//芯片擦除
	FLASH_CS_HIGH;
	Flash_Wait_Nobusy();//等待写入结束
}

//进入掉电模式
void Flash_Power_Down(void)
{
	FLASH_CS_LOW;
	Spi_Write_Byte(FLASH_POWER_DOWN);//掉电指令
	FLASH_CS_HIGH;
}

//唤醒
void Flash_Wake_Up(void)
{
	FLASH_CS_LOW;
	Spi_Write_Byte(FLASH_RELEASE_POWER_DOWN);//唤醒指令
	FLASH_CS_HIGH;
}

//读取器件ID
uint16_t Flash_Read_Device_ID(void)
{
	uint16_t ID=0;
	
	FLASH_CS_LOW;
	Spi_Write_Byte(FLASH_READ_DEVICE_ID);
	Spi_Write_Byte(0x00);//写入24位地址;假地址
	Spi_Write_Byte(0x00);
	Spi_Write_Byte(0x00);
	ID |= Spi_Read_Byte()<<8;
	ID |= Spi_Read_Byte();
	FLASH_CS_HIGH;
	
	return ID;
}

//读取厂商ID
uint32_t Flash_Read_JEDEC_ID(void)
{
	uint32_t ID=0;
	
	FLASH_CS_LOW;
	Spi_Write_Byte(FLASH_READ_JEDEC_ID);
	ID |= Spi_Read_Byte()<<16;
	ID |= Spi_Read_Byte()<<8;
	ID |= Spi_Read_Byte();
	FLASH_CS_HIGH;
	
	return ID;
}
//测试
void Flash_Init_Test()
{
	flash_pt.flash_id = Flash_Read_Device_ID();
	Lcd_Display_String(0,35,"id:");
	Lcd_Display_Num(0,65,flash_pt.flash_id,5,16);//读出Flash的设备ID=0XEF13(61203)
}

//FLASH循环擦写测试
void Flash_Circle_Test(void)
{
	uint16_t i;
	uint32_t Addr;
	
	LCD_Display_Clear();
	for(i=0;i<256;i++)
	{
		tx_data[i] = 'A';	
	}
	
	Lcd_Display_OneChar(0, 0, tx_data[255]);
	Flash_Write_Enable();	//写使能
	//分16次写入4096字节(1扇区=16x256)
	for(i=0;i<2;i++)
	{
		Addr = 256 * i;
		Flash_Write_Enable();//经过写操作,擦除后,必须使能写,才能写入
		Flash_Write_NoCheck(tx_data,Addr,256);
		//Flash_Write_NoCheck(tx_data,0,1023);//无检测写可以直接读出,每次写入不超过256字节
		//Flash_Write_Byte(tx_data,Addr,255);//检查写需要写入后屏蔽(注释掉),再读出来
		nrf_delay_ms(100);
		Lcd_Display_String(2,1,"flash write...");
	}
		Addr = 0;	
		Lcd_Display_String(4,1,"flash write ok");
		//Flash_Read_Byte(rx_data+512,512,256);//注意写入地址偏移
		//Flash_FastRead_Byte(rx_data+512,512,256);
		Flash_Read_Byte(rx_data,0,512);//读出一扇区数据
		Lcd_Display_OneChar(0, 12, rx_data[500]);
		Flash_Erase_Sector(0);//擦除第一个扇区
		nrf_delay_ms(100);
		Lcd_Display_String(6,64,"erase ok");
		cnt++;
		Lcd_Display_Num(6,0,cnt,6,16); //显示读写次数
	
	//写入读出是否正确测试
	/*	if((rx_data[0]=='A')&&(rx_data[254]=='A'))
		{
			Lcd_Display_String(4,1,"flash write ok");
		}
		else
		{
			Lcd_Display_String(4,1,"flash write err");
		}*/
}

参考:

1.STM32F10x_SPI(硬件接口 + 软件模拟)读写Flash(25Q16)

2.SPI—读写串行 FLASH

3.Flash存储W25Q16芯片

### 关于NRF52832芯片的信息 NRF52832是一款由 Nordic Semiconductor 推出的低功耗蓝牙(Bluetooth Low Energy, BLE)SoC,广泛应用于物联网设备和其他无线通信场景中。该芯片基于ARM Cortex-M0+处理器架构设计,集成了2.4GHz收发器和支持多种协议栈的功能[^1]。 #### 芯片规格书 NRF52832 的官方规格书中详细描述了其硬件特性、寄存器配置以及外设功能等内容。主要特点如下: - **处理能力**:采用 ARM Cortex-M0+ 处理核心,运行频率可达 64MHz。 - **存储容量**:内置 512KB Flash 和 64KB SRAM,满足大多数嵌入式应用程序需求。 - **射频性能**:支持 Bluetooth v4.x 和 v5 协议标准;具有高灵敏度接收器 (-97dBm @ 1Mbps),适合远距离传输应用环境。 - **外围接口**:提供 SPI/I²C/UART/PWM等多种通用串行总线选项,并兼容 USB 设备模式操作通过 WebUSB 实现便捷调试过程。 #### 开发套件 为了方便开发者测试和开发基于 NRF52832 平台的应用项目,厂商提供了专门针对此款微控制器系列优化过的 DK (Development Kit) 工具包——nRF52 Development Kits 。这些工具通常包括但不限于以下组件: - 主板电路板上安装有一个预编程好的目标 MCU(NRF52832), 可直接用于原型验证; - 板载 J-link debugger/emulator ,便于在线烧录固件并实时监控程序执行状态; - 支持 Segger Embedded Studio IDE 或其他第三方软件平台完成整个产品生命周期内的编码作业[^2]。 #### 示例代码 对于初学者而言,在实际动手实践之前查看一些典型例子有助于理解具体实现方法论。下面给出一段简单的 LED 点亮控制示范片段: ```c #include "nrf_gpio.h" #define LED_PIN 17 void delay_ms(uint32_t ms){ uint32_t i; for(i=0;i<ms*1000;i++); } int main(void){ nrf_gpio_cfg_output(LED_PIN); // 设置 GPIO 引脚方向为输出 while(1){ nrf_gpio_pin_set(LED_PIN); delay_ms(500); nrf_gpio_pin_clear(LED_PIN); delay_ms(500); } } ``` 上述源码展示了如何利用 SDK 提供的基础 API 函数来操控指定编号物理端口上的电平变化情况从而达到驱动外部负载的目的。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值