历史上的今天
返回首页

历史上的今天

今天是:2025年01月04日(星期六)

2019年01月04日 | stm32 SPI-FLASH W25Q64

2019-01-04 来源:eefocus

W25Q64


1

2

3


SPI配置步骤

1.使能SPI时钟
2.使能GPIO端口时钟
3.初始化GPIO,配置引脚模式
4.初始化SPI
5.使能SPI
6.SPI读写数据
7.查看SPI传输状态


1


举例

1
2
3

1
2

3


typedef struct

{

uint16_t SPI_Direction;

uint16_t SPI_Mode; 

uint16_t SPI_DataSize; //数据帧格式

uint16_t SPI_CPOL; //时钟极性

uint16_t SPI_CPHA; //时钟相位

uint16_t SPI_NSS; //软件从设备管理

uint16_t SPI_BaudRatePrescaler; //波特率控制

uint16_t SPI_FirstBit; //帧格式 

uint16_t SPI_CRCPolynomial; //CRC多项式寄存器

}SPI_InitTypeDef;


#define EN25X_WriteEnable 0x06

#define EN25X_WriteDisable 0x04 

#define EN25X_ReadStatusReg 0x05 

#define EN25X_WriteStatusReg 0x01 

#define EN25X_ReadData 0x03 

#define EN25X_FastReadData 0x0B 

#define EN25X_FastReadDual 0x3B 

#define EN25X_PageProgram 0x02 

#define EN25X_BlockErase 0xD8 

#define EN25X_SectorErase 0x20 

#define EN25X_ChipErase 0xC7 

#define EN25X_PowerDown 0xB9 

#define EN25X_Continue_Read 0xFF 

#define EN25X_ReleasePowerDown 0xAB 

#define EN25X_DeviceID 0xAB 

#define EN25X_ManufactDeviceID 0x90 

#define EN25X_JedecDeviceID 0x9F 


void SPI_init()

{

GPIO_InitTypeDef gpio = 

{

GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15,

GPIO_Speed_50MHz,

GPIO_Mode_AF_PP

};


SPI_InitTypeDef spi = 

{

SPI_Direction_2Lines_FullDuplex,

SPI_Mode_Master, //0x0104

SPI_DataSize_8b,

SPI_CPOL_High,

SPI_CPHA_2Edge,

SPI_NSS_Soft,

SPI_BaudRatePrescaler_256,

SPI_FirstBit_MSB,

7

};


RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2, ENABLE); //使能SPI时钟

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); //使能GPIO端口时钟


GPIO_Init(GPIOB, &gpio); //初始化GPIO,配置引脚模式


SPI_Init(SPI2, &spi); //初始化SPI


SPI_Cmd(SPI2, ENABLE); //使能SPI

}


void SPI2_SetSpeed(u8 rate)

{

SPI2->CR1 &= 0xFFC7;

SPI2->CR1 |= rate;

SPI_Cmd(SPI2, ENABLE);


void EN25Q64_init()

{

GPIO_InitTypeDef gpiob = 

{

GPIO_Pin_12,

GPIO_Speed_50MHz,

GPIO_Mode_Out_PP

};


GPIO_InitTypeDef gpiog13 = 

{

GPIO_Pin_13,

GPIO_Speed_50MHz,

GPIO_Mode_Out_PP

};


GPIO_InitTypeDef gpiog14 = 

{

GPIO_Pin_13,

GPIO_Speed_50MHz,

GPIO_Mode_Out_PP

};


GPIO_InitTypeDef gpiof = 

{

GPIO_Pin_9,

GPIO_Speed_50MHz,

GPIO_Mode_Out_PP

};


RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB | RCC_APB2Periph_GPIOG | RCC_APB2Periph_GPIOF, ENABLE);


GPIO_Init(GPIOB, &gpiob);

SPI2_NSS = 1; //以太网模块片选(拉高,防止干扰)


GPIO_Init(GPIOG, &gpiog13);

FLASH_CS = 1; //FLASH片选


GPIO_Init(GPIOG, &gpiog14);

SD_CS = 1; //SD卡模块片选


GPIO_Init(GPIOF, &gpiof);

TUB_4 = 1; //NRF24L01模块片选


SPI_init();

}


u8 SPI_read_write(u16 d) //SPI读写数据

{

while(SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_TXE) != SET);


SPI_I2S_SendData(SPI2, d);


while(SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_RXNE) != SET);


return SPI_I2S_ReceiveData(SPI2);

}


u16 EN25Q64_read_id()

{

u16 r;


FLASH_CS = 0;


SPI_read_write(EN25X_ManufactDeviceID);

SPI_read_write(0); //dummy

SPI_read_write(0);

SPI_read_write(0);


r = SPI_read_write(EN25X_Continue_Read) << 8;

r = SPI_read_write(EN25X_Continue_Read);


FLASH_CS = 1;


return r;

}


void EN25Q64_read_unique_id(u8 *id)

{

int i = 0;


FLASH_CS = 0;


SPI_read_write(0x4B);

SPI_read_write(0); //dummy

SPI_read_write(0);

SPI_read_write(0);

SPI_read_write(0);


for(i = 0; i < 8; i++)

{

id[i] = SPI_read_write(0xFF);

}


    FLASH_CS = 1;

}


u8 EN25Q64_read_status()

{

u8 r;


FLASH_CS = 0;


SPI_read_write(EN25X_ReadStatusReg);

r = SPI_read_write(EN25X_Continue_Read);


FLASH_CS = 1;


return r;

}


void EN25Q64_read(u8 *buf, u32 addr, u16 num)

{

u16 i = 0;


FLASH_CS = 0;


SPI_read_write(EN25X_ReadData);

SPI_read_write((u8)(addr) >> 16); //24bit地址

SPI_read_write((u8)(addr) >> 8);

SPI_read_write((u8)addr);


for(i = 0; i < num; i++)

{

buf[i] = SPI_read_write(EN25X_Continue_Read);

}


FLASH_CS = 1;

}


void EN25Q64_write_enable()

{

FLASH_CS = 0;


SPI_read_write(EN25X_WriteEnable);


FLASH_CS = 1;

}


void EN25Q64_write_disable()

{

FLASH_CS = 0;


SPI_read_write(EN25X_WriteEnable);


FLASH_CS = 1;

}


void EN25Q64_write_status(u8 s)

{

FLASH_CS = 0;


SPI_read_write(EN25X_WriteDisable);

SPI_read_write(s);


FLASH_CS = 1;

}


void EN25Q64_is_busy()

{

while((EN25Q64_read_status() & 0x01) == 0x01); //BUSY

}


void EN25Q64_write_page(u8 *buf, u32 addr, u16 num) //页写

{

u16 i = 0;


EN25Q64_write_enable();


FLASH_CS = 0;


SPI_read_write(EN25X_PageProgram); //Page Program

SPI_read_write(addr >> 16);

SPI_read_write(addr >> 8);

SPI_read_write(addr);


for(i = 0; i < num; i++)

{

SPI_read_write(buf[i]);

}


FLASH_CS = 1;


EN25Q64_is_busy();

}


void EN25Q64_write_nocheck(u8 *buf, u32 addr, u16 num)

{

u8 *b = buf;

u32 a = addr;

u16 n = num;


n = 256 - (num % 256); //写满一页


while(1)

{

EN25Q64_write_page(b, a, n);


if(n == num) 

{

break;

}


b += n;

a += n;

num -= n;


if(num > 256) 

{

n = 256;

}

else

{

n = num;

}

}

}


void EN25Q64_sector_erase(u32 a)

{

EN25Q64_write_enable();

EN25Q64_is_busy();


FLASH_CS = 0;


SPI_read_write(EN25X_SectorErase);

SPI_read_write((u8)a >> 16);

SPI_read_write((u8)a >> 8);

SPI_read_write(a);


FLASH_CS = 1;


EN25Q64_is_busy();

}


u8 EN25QXX_BUF[4096];

void EN25Q64_write(u8 *buf, u32 addr, u16 num)

{

u16 i;

u32 pos;

u16 offset;

u16 n;

u8 *b = buf;


pos = addr / 4096; //扇区位置

offset = addr % 4096;

n = 4096 - offset ; //写满扇区


if(num <= n)

{

n = num;

}


while(1) 

{

EN25Q64_read(EN25QXX_BUF, pos * 4096, 4096); //读出整个扇区


EN25Q64_sector_erase(pos); //擦除整个扇区


for(i = 0; i < n; i++)

{

EN25QXX_BUF[i + offset] = b[i];

}


EN25Q64_write_nocheck(EN25QXX_BUF, pos * 4096, 4096); //写入整个扇区


if(n == num)

{

break;

}


pos++;

num -= n;

offset = 0;

b += n; 

if(num > 4096)

{

n = 4096;

}

else

{

n = num;

}

}

}


void EN25Q64_chip_erase()

{

EN25Q64_write_enable();

EN25Q64_is_busy();


FLASH_CS = 0;


SPI_read_write(EN25X_ChipErase);


FLASH_CS = 1;


EN25Q64_is_busy();

}


void EN25Q64_power_down()

{

FLASH_CS = 0;


SPI_read_write(EN25X_PowerDown);


FLASH_CS = 1;


delay_us(3);

}


void EN25Q64_release_power_down()

{

FLASH_CS = 0;


SPI_read_write(EN25X_ReleasePowerDown);


FLASH_CS = 1;


delay_us(3);

}


推荐阅读

史海拾趣

Grand Halo Technology Co Ltd公司的发展小趣事

对于自动关灯开关电路,网友们可能会提出多种问题,这里列出一些常见问题及其回答:

1. 自动关灯是如何实现的?

回答: 自动关灯通常通过传感器(如光敏电阻、红外传感器、人体感应传感器等)和电路控制来实现。例如,使用光敏电阻可以根据环境光线变化自动开关灯;人体感应传感器则在检测到人体移动时开启灯光,并在一定时间内无人移动时自动关闭。

2. 需要哪些主要元件来构建一个简单的自动关灯电路?

回答: 构建一个简单的自动关灯电路,你可能需要光敏电阻(或相应传感器)、继电器(或晶体管等开关元件)、电阻、电容(用于去抖动或延时)、电源(如电池或直流适配器)、以及LED灯或实际的白炽灯/荧光灯作为负载。对于更复杂的人体感应型,还需要红外传感器模块。

3. 如何调节自动关灯的延时时间?

回答: 调节自动关灯的延时时间通常通过改变电路中的电容或电阻值来实现。在延时电路中,电容充电或放电的时间决定了延时长度。增加电容值或增大充电电阻值可以延长延时时间,反之则缩短。

4. 自动关灯电路是否节能?

回答: 是的,自动关灯电路在无人或光线充足时自动关闭灯光,从而有效减少不必要的电能消耗,达到节能的目的。这对于长时间无人使用或光线条件良好的场所尤为有效。

5. 如何保证自动关灯电路的稳定性和可靠性?

回答: 要保证自动关灯电路的稳定性和可靠性,首先应选择质量可靠的元件;其次,合理设计电路布局,避免元件间相互干扰;此外,还可以加入过流保护、过压保护等安全措施;最后,进行充分的测试,确保电路在不同环境条件下都能正常工作。

6. 能否将自动关灯电路与智能家居系统连接?

回答: 是的,许多现代自动关灯电路都支持智能家居系统集成。通过WiFi、蓝牙或Zigbee等无线通信技术,可以将自动关灯开关连接到智能家居中心控制器上,实现远程控制、定时开关、场景模式切换等功能,使家居生活更加智能化和便捷。

长运通(CYT)公司的发展小趣事

长运通注重与高校和研究机构的合作,积极引进和培养人才。公司与电子科技大学、西安电子科技大学等知名高校建立了紧密的合作关系,共同开展技术研发和人才培养。通过与高校的合作,长运通不仅获得了更多的技术资源支持,也为公司培养了一批高素质的研发人才。这些人才为公司的发展提供了强有力的支撑。

ERGOBAHCO公司的发展小趣事

ERGOBAHCO公司成立于20世纪90年代初,当时正值电子行业快速发展的黄金时期。公司创始人李明(化名)看准了市场对于高质量电子配件的需求,决定从电子连接器这一细分领域入手。然而,初创时期公司面临着资金短缺、技术落后等诸多挑战。李明凭借对市场敏锐的洞察力,成功争取到了几笔关键的投资,并带领团队攻克了一系列技术难关。通过不懈努力,ERGOBAHCO公司逐渐在电子连接器领域站稳了脚跟。

Gem Asia Enterprise Co Ltd公司的发展小趣事

随着业务的不断扩展,Gem Asia意识到全球化布局的重要性。公司开始在全球范围内寻找合适的生产基地和市场机会。通过并购海外优质资产、建立海外研发中心和销售网络,Gem Asia成功实现了从本土企业向跨国公司的转变。特别是在亚洲和欧美市场,Gem Asia凭借其高质量的产品和灵活的市场策略,赢得了广泛的客户认可和市场份额。

DATEL Inc公司的发展小趣事

随着技术的不断进步,DATEL Inc.的数据采集产品逐渐在军事领域找到了用武之地。公司接到一项为军方开发高精度数据采集系统的订单。经过深入研究和反复测试,DATEL Inc.成功交付了一套满足军方严苛要求的数据采集系统,为军方提供了准确、可靠的数据支持。这一突破不仅为DATEL Inc.赢得了军方的高度认可,也进一步扩大了公司在军事领域的影响力。

bb-smartworx公司的发展小趣事

随着技术的不断进步,DATEL Inc.的数据采集产品逐渐在军事领域找到了用武之地。公司接到一项为军方开发高精度数据采集系统的订单。经过深入研究和反复测试,DATEL Inc.成功交付了一套满足军方严苛要求的数据采集系统,为军方提供了准确、可靠的数据支持。这一突破不仅为DATEL Inc.赢得了军方的高度认可,也进一步扩大了公司在军事领域的影响力。

问答坊 | AI 解惑

液晶显示模块编程

MG-12232模块的编程   在系统程序的初始化部分,应对程序中用到的寄存器和临时变量作说明,如: unsigned  char        TRANS; unsigned  char        PAGEADD;&nbs ...…

查看全部问答>

如何诊断和排除电喷发动机加速不良的故障?

一、故障现象。踩下加速踏板后发动机转速不能马上升高,有迟滞现象,加速反应迟缓或在加速过程中发现有轻微的波动。 二、故障原因 (1)点火提前角不正确。 (2)燃油压力过低。 (3)进气系统中有漏气。 (4)节气门位置 ...…

查看全部问答>

开赛后,论坛是否会发布相关解题策略?

本帖最后由 paulhyde 于 2014-9-15 09:03 编辑 如题。。 新手啊~~许多都不会做,希望论坛到时能相助。。 只要能在比赛期间,做出作品,我已经是万分感激了。。 --------------------------------------------soso----------------------------- ...…

查看全部问答>

求助: IDS语音器件分段地址的获取

各位大虾:你们好!上次的帖子问题没说好,重新发一下!!   再下最近在做一个基于单片机的语音播报时钟,但是在语音器件分段地址的获取遇到了问题!主要功能是通过按键播报当前系统时间,我选用的语音芯片是ISD2560,用的AT89S51单片机, ...…

查看全部问答>

vxworks的DHCP客户端问题

我现在有两个网卡,分别在不同的网络里,IP地址的分别是通过DHCP服务器自动分配,设计了一套DHCP客户端申请程序,单个网口可以实现IP地址的动态分配,串行方式两个网口都可动态获得IP地址,能不能实现两个网口同时动态申请IP地址哪?这样可以节约时 ...…

查看全部问答>

wince webservice c#问题

大家好,我创建了一个最简单的hello world webservice,将其添加作为web引用到一个wince应用程序中,调试运行时报参数超出范围异常(argumentoutofrangeexception),报异常语句是:  object[] results = this.Invoke(\"HelloWorld\", ne ...…

查看全部问答>

wince 5.0 播放声音问题

PlaySound(L\"1.wav\",NULL, SND_ASYNC|SND_NODEFAULT ); 看我这句话 有什么错误呢 把1.wav放在了 和exe文件同目录下,但是 为什么 没有播放呢?…

查看全部问答>

如何修改按钮背景色, 对话框。VC++

普通按钮控件。 我要实现的是通过按钮1,修改按钮2的背景色。如何实现。…

查看全部问答>

请问如何更改sd card 序列号(serial number)

请问如何更改sd card 序列号(serial number),要用什么程序?如何更改?…

查看全部问答>

Synopsys DC 安装时遇到的问题

进入“Synopsys SSS Feature Keygen”,运行命令“sssverify.exe  synopsys.dat”,此时需要生成一串20个字符,但是运行之后并没有出现20个字符,而是出现以下的东西  …

查看全部问答>