​​​​​​​STM32通过SPI硬件读写W25Q64

news2024/9/16 8:57:19

目录

STM32通过SPI硬件读写W25Q64

1. STM32的SPI外设简介

2. STM32的SPI框图

2.1 数据寄存器和移位寄存器(左上角部分)

控制逻辑(其余右下角的部分)

3.STM32的SPI基本框图

4. STM32的SPI主模式全双工连续传输 时序图

5. STM32的SPI主模式全双工非连续传输 时序图

6. STM32非连续传输时,不同时钟速率的区别

256分频

128分频

64分频

2分频(32MHZ)

7. 编写SPI硬件读写W25Q64注意事项

8. STM32的SPI外设相关库函数(只看SPI的)

7.编写SPI硬件读写W25Q64步骤

8. 编写STM32SPI主模式全双工非连续传输

8.1 步骤

8.2 程序文件简要说明:

MySPI.c(相对软件,硬件仅需修改此文件)

MySPI.h

W25Q64.c

W25Q64.h

W25Q64_Ins.h

main.c


SPI通信详解可以看这篇文章:SPI通信详解-CSDN博客

SPI软件通信读写W25Q64可以看这篇文章:STM32通过SPI软件读写W25Q64-CSDN博客

STM32通过SPI硬件读写W25Q64

1. STM32的SPI外设简介

  • STM32内部集成了硬件SPI收发电路,可以由硬件自动执行时钟生成、数据收发等功能,减轻CPU的负担

  • 可配置8位/16位数据帧、高位先行/低位先行

  • 时钟频率:fpclk/(2,4,8,16,32,64,128,256) PCLK指的是总线时钟,比如APB1 APB2 所以最快的速度就是分频2了。

  • 支持多主机模型、主或从操作

  • 可精简为半双工/单工通信(很少用)

  • 支持DMA

  • 兼容12S协议(音频专用)

  • STM32F103C8T6 硬件SPI资源:SPI1、SPI2(SPI1为APB2外设)(其他位APB1)

2. STM32的SPI框图

这个框图是在做主机时的框图、移位寄存器右移

2.1 数据寄存器和移位寄存器(左上角部分)

这里和I2C、串口的设计是异曲同工的。

他们的目的都是实现连续的数据流

  1. 移位寄存器
    • 移位寄存器可以由LSBFIRST控制位 控制是左移还是右移(0高位先、1低位先)
    • 此时电路为右移状态。也就是低位先行
  2. MOSI和MISO
    • 数据通过移位寄存器 一位一位 的从MOSI移出去。
    • MISO的数据,一位一位的 移入到移位寄存器
    • 输出部分的MOSI和MISO进行了交叉,这个是为了主从模式变换的
  3. 接收缓冲区 和发送缓冲区
    • 接收缓冲区叫做RDR
    • 发送缓冲区叫做TDR
    • TDR和RDR占用同一个地址,统一叫做DR
    • **在TDR转入移位寄存器时会产生TXE标志位为1。表示发送寄存器为空。**这时候就可以填装下一个字节了。实现不间断的连续发送
    • 在移位寄存器移出完成时,数据移入也会同步完成。这时移入的数据会整体的转入到接收缓冲区RDR。**接收到后,会置RXNE标志位为1。表示接收寄存器非空。**这时就可以在下一个数据来之前,读出RDR。实现连续的接收

控制逻辑(其余右下角的部分)

  1. 波特率发生器

    • 输入时钟为PCLK (36M或者72M)

    • 经过分频之后输出到SCK引脚

    • 与移位寄存器同步。每产生一个时钟,移位寄存器移入移出一个bit

    • CR1寄存器的BR0、BR1、BR2用来控制分频系数。写入不同的值,可以对PCLK时钟执行2~256分频。得到SCK时钟

      000 /2001 /4010 /8011 /16
      100 /32101 /64110 /128111 /256
  2. 其他的通信电

    这些电路都是黑盒子电路。这里挑选几个重点的讲

    • LSBFIRST — 决定高位先行还是低位先行
    • SPE —(SPI ENABLE)决定SPI使能
    • BR —(Baud Rate)配置波特率,也就是SCK
    • MSTR —(Master)配置主从模式
    • CPOL和CPHA — 选择SPI的四种模式

    CR1寄存器

    • TXE — 发送寄存器空
    • RXNE — 接收寄存器非空

    CR2寄存器

    • 中断使能..DMA使能等
  3. 最后电路的左下角还有一个NSS位也就是SS位(不过我们一般直接用GPIO模拟,这个位置更偏向用于使用到多主机时采用的)

    简要了解一下

    • 多主机时,所有的主机连接到一条NSS线上。当别的NSS置0时。代表现在别人已经是主机了。会沿着线到一个数据选择器。告诉这个机器,已经有设备是主机了。自己不能跟他抢。

3.STM32的SPI基本框图

4. STM32的SPI主模式全双工连续传输 时序图

5. STM32的SPI主模式全双工非连续传输 时序图

这里可以理解为,一个一个发,发完一个收一个。收完之后再发下一个

并不会像连续一样 提前写入一个字节候着。会造成一定的资源浪费,拖慢传输速度

  • 适用于对传输速率要求不高的场合。

6. STM32非连续传输时,不同时钟速率的区别

因为在非连续传输时,数据不是连续的流。所以在软件读取后才发送会浪费很长时间。

对于CLK时钟频率越快的情况下,造成资源浪费的情况就显得越严重

256分频

128分频

64分频

2分频(32MHZ)

这里是因为他的频率已经超过我的示波器的采样频率了。但是也是可以看个大概的

可以看到 ,时间浪费就很严重。

7. 编写SPI硬件读写W25Q64注意事项

  1. 硬件SPI外设不能随意指定,只能看复用到的引脚列表。

    其中NSS片选引脚可以自己指定。用他指定的也可以

8. STM32的SPI外设相关库函数(只看SPI的)

void SPI_I2S_DeInit(SPI_TypeDef* SPIx);

  • I2S 恢复缺省配置

void SPI_Init(SPI_TypeDef* SPIx, SPI_InitTypeDef* SPI_InitStruct);

  • SPI 初始化

void I2S_Init(SPI_TypeDef* SPIx, I2S_InitTypeDef* I2S_InitStruct);

  • I2S 初始化

void SPI_StructInit(SPI_InitTypeDef* SPI_InitStruct);

  • SPI 初始化

void I2S_StructInit(I2S_InitTypeDef* I2S_InitStruct);

void SPI_Cmd(SPI_TypeDef* SPIx, FunctionalState NewState);

  • SPI 外设使能

void I2S_Cmd(SPI_TypeDef* SPIx, FunctionalState NewState);

  • I2S 使能

void SPI_I2S_ITConfig(SPI_TypeDef* SPIx, uint8_t SPI_I2S_IT, FunctionalState NewState);

  • I2S 中断配置

void SPI_I2S_DMACmd(SPI_TypeDef* SPIx, uint16_t SPI_I2S_DMAReq, FunctionalState NewState);

  • I2S DMA配置

void SPI_I2S_SendData(SPI_TypeDef* SPIx, uint16_t Data);

  • I2S 写数据到TDR寄存器(发送)

uint16_t SPI_I2S_ReceiveData(SPI_TypeDef* SPIx);

  • I2S 读DR数据寄存器(接收)

void SPI_NSSInternalSoftwareConfig(SPI_TypeDef* SPIx, uint16_t SPI_NSSInternalSoft);

  • NSS引脚配置

void SPI_SSOutputCmd(SPI_TypeDef* SPIx, FunctionalState NewState);

  • NSS引脚配置

void SPI_DataSizeConfig(SPI_TypeDef* SPIx, uint16_t SPI_DataSize);

  • 8位或16位数据帧配置

void SPI_TransmitCRC(SPI_TypeDef* SPIx);

  • CRC校验配置

void SPI_CalculateCRC(SPI_TypeDef* SPIx, FunctionalState NewState);

  • CRC校验配置

uint16_t SPI_GetCRC(SPI_TypeDef* SPIx, uint8_t SPI_CRC);

  • CRC校验配置

uint16_t SPI_GetCRCPolynomial(SPI_TypeDef* SPIx);

  • CRC校验配置

void SPI_BiDirectionalLineConfig(SPI_TypeDef* SPIx, uint16_t SPI_Direction);

  • 半双时,双向线方向配置

FlagStatus SPI_I2S_GetFlagStatus(SPI_TypeDef* SPIx, uint16_t SPI_I2S_FLAG);

  • 获取标志位

void SPI_I2S_ClearFlag(SPI_TypeDef* SPIx, uint16_t SPI_I2S_FLAG);

  • 清除标志位

ITStatus SPI_I2S_GetITStatus(SPI_TypeDef* SPIx, uint8_t SPI_I2S_IT);

  • 获取中断标志位

void SPI_I2S_ClearITPendingBit(SPI_TypeDef* SPIx, uint8_t SPI_I2S_IT);

  • 清除中断标志位

7.编写SPI硬件读写W25Q64步骤

  1. 开启GPIO和SPI外设时钟
  2. 初始化GPIO口
    • MISO为上拉输入,SCK、MOSI复用推挽输出
    • SS 通用推挽输出
  3. 配置SPI外设
    • 用结构体即可
  4. 开关控制
    • 使能
  5. 参考时序图编写代码

8. 编写STM32SPI主模式全双工非连续传输

8.1 步骤

  1. 等待发送寄存器为空标志位TXE = 1
  2. 软件写入数据到发送寄存器
  3. 等待接收完成(这时发送也一定完成)
  4. 读取、返回RDR
  • !!注意其中的软件清除 是不需要我们手动清除的。 因为在我们写入TDR时会顺便清除TXE标志位、读取RDR时会清除RXNE标志位

所以这个只需要把底层的MySPI.c代码改一下就可以了

8.2 程序文件简要说明:

  • MySPI:完成SPI的三个基本时序
  • MySPI.h:函数声明
  • W25Q64.c:初始化W25Q64存储寄存器。完成读写、擦除存储器的时序
  • W25Q64.h:函数声明、数据结构体声明
  • W25Q64_Ins.h:控制存储器时需要用到的指令集
  • main.c:测试硬件SPI读取存储器结果。

MySPI.c(相对软件,硬件仅需修改此文件)

#include "stm32f10x.h"                  // Device header

/*所用引脚列表*/
#define RCC_GPIO        RCC_APB2Periph_GPIOA
#define RCC_SPI1        RCC_APB2Periph_SPI1
#define SCK_Port        GPIOA
#define SCK_Pin         GPIO_Pin_5
#define SS_Port         GPIOA
#define SS_Pin          GPIO_Pin_4
#define MOSI_Port       GPIOA
#define MOSI_Pin        GPIO_Pin_7
#define MISO_Port       GPIOA
#define MISO_Pin        GPIO_Pin_6

/**
  * 函    数:写片选信号SS
  * 参    数:BitValue:输入1片选信号SS为高电平
  * 返 回 值:无
  * 注意事项:无
  */
void MySPI_W_SS (uint8_t BitValue)
{
    GPIO_WriteBit(SS_Port,SS_Pin,(BitAction)BitValue);
}

/**
  * 函    数:MySPI初始化
  * 参    数:无
  * 返 回 值:无
  * 注意事项:无
  */
void MySPI_Init(void)
{   
    /*配置SPI、GPIO外设时钟*/
    RCC_APB2PeriphClockCmd(RCC_SPI1,ENABLE);
    RCC_APB2PeriphClockCmd(RCC_GPIO,ENABLE);
    /*配置引脚*/
    GPIO_InitTypeDef GPIO_InitStructure;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_InitStructure.GPIO_Pin = SCK_Pin;
    GPIO_Init(SCK_Port,&GPIO_InitStructure);    //时钟配置为 复用 推挽输出
    
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_InitStructure.GPIO_Pin = MOSI_Pin;
    GPIO_Init(MOSI_Port,&GPIO_InitStructure);   //MOSI配置为 复用 推挽输出
    
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_InitStructure.GPIO_Pin = SS_Pin;
    GPIO_Init(SS_Port,&GPIO_InitStructure);     //片选配置为 通用 推挽输出
    
    
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
    GPIO_InitStructure.GPIO_Pin = MISO_Pin;
    GPIO_Init(MISO_Port,&GPIO_InitStructure);   //MISO配置为 上拉输入
    
    /*配置SPI*/
    SPI_InitTypeDef SPI_InitSturcture;
    SPI_InitSturcture.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_128;//分频系数
    SPI_InitSturcture.SPI_CPHA = SPI_CPHA_1Edge;    //第1个边沿采样(读入)
    SPI_InitSturcture.SPI_CPOL = SPI_CPOL_Low;      //CSK默认低电平    == 模式0
    SPI_InitSturcture.SPI_CRCPolynomial = 7;         //指定CRC计算的多项式(默认为7)
    SPI_InitSturcture.SPI_DataSize = SPI_DataSize_8b;//8位数据帧
    SPI_InitSturcture.SPI_Direction = SPI_Direction_2Lines_FullDuplex;  //两根线全双工
    SPI_InitSturcture.SPI_FirstBit = SPI_FirstBit_MSB;//高位先行
    SPI_InitSturcture.SPI_Mode = SPI_Mode_Master;   //主机模式
    SPI_InitSturcture.SPI_NSS = SPI_NSS_Soft;   //软件NSS
    SPI_Init(SPI1, &SPI_InitSturcture);
    
    /*使能SPI*/
    SPI_Cmd(SPI1,ENABLE);
    
    MySPI_W_SS(1);//初始化不选中从机
}

/*******************/
/*SPI的三个时序单元*/
/*******************/

/**
  * 函    数:起始信号
  * 参    数:无
  * 返 回 值:无
  * 注意事项:无
  */
void MySPI_Start(void)
{   
    MySPI_W_SS(0);
}

/**
  * 函    数:终止条件
  * 参    数:无
  * 返 回 值:无
  * 注意事项:无
  */
void MySPI_Stop(void)
{   
    MySPI_W_SS(1);
}

/**
  * 函    数:交换一个字节(模式0)(非连续)
  * 参    数:SendByte      待发送的字节
  * 返 回 值:ReceiveByte   接收到的字节
  * 注意事项:这是使用了移位模型的方式。效率更快
              如果要改为模式1,则先上升沿再发送。先下降沿再接收(2、3则直接改时钟极性就ok了)
  */
uint8_t MySPI_WarpByte(uint8_t SendByte)
{
    //等待TXE标志位为1 ,发送寄存器空
    while(SPI_I2S_GetFlagStatus(SPI1,SPI_I2S_FLAG_TXE) != SET);
    //软件写入数据到发送寄存器
    SPI_I2S_SendData(SPI1,SendByte);
    //等待接收完成(这时发送也一定完成)
    while(SPI_I2S_GetFlagStatus(SPI1,SPI_I2S_FLAG_RXNE) != SET);
    //读取RDR
    return SPI_I2S_ReceiveData(SPI1);
} 

MySPI.h

#ifndef __MYSPI_H
#define __MYSPI_H

//初始化
void MySPI_Init(void);
//起始
void MySPI_Start(void);
//终止
void MySPI_Stop(void);
//交换
uint8_t MySPI_WarpByte(uint8_t SendByte);

#endif

W25Q64.c

#include "stm32f10x.h"                  // Device header
#include "W25Q64.h"
#include "W25Q64_Ins.h"
#include "MySPI.h"

/**
  * 函    数:初始化W25Q64
  * 参    数:无
  * 返 回 值:无
  * 注意事项:无
  */
void W25Q64_Init(void)
{
    MySPI_Init();
}

/********************/
/*拼接完整的通信时序*/
/********************/

/**
  * 函    数:查看W25Q64的厂商号和设备号
  * 参    数:ID* Str   存放了ID结构体的指针
  * 返 回 值:无
  * 注意事项:接收第八位时是|=
  */
ID W25Q64_ID;//存放设备ID号的结构体
void W25Q64_ReadID(ID* Str)
{
    MySPI_Start();//起始
    MySPI_WarpByte(W25Q64_JEDEC_ID);//发送读取设备号指令。返回值不要
    Str->MID = MySPI_WarpByte(W25Q64_DUMMY_BYTE);//接收厂商ID 从机发来的设备号。发送值随便
    Str->DID = MySPI_WarpByte(W25Q64_DUMMY_BYTE);//接收设备ID高八位
    Str->DID <<= 8;//把接收到的数据放到高八位
    Str->DID |= MySPI_WarpByte(W25Q64_DUMMY_BYTE);//接收设备ID低八位
    MySPI_Stop();//停止
}

/**
  * 函    数:写使能
  * 参    数:无
  * 返 回 值:无
  * 注意事项:无
  */
void W25Q64_WriteEnable(void)
{
    MySPI_Start();//起始
    MySPI_WarpByte(W25Q64_WRITE_ENABLE);//发送
    MySPI_Stop();//停止
}

/**
  * 函    数:写失能
  * 参    数:无
  * 返 回 值:无
  * 注意事项:无
  */
void W25Q64_WriteDisable(void)
{
    MySPI_Start();//起始
    MySPI_WarpByte(W25Q64_WRITE_DISABLE);//发送
    MySPI_Stop();//停止
}

/**
  * 函    数:等待忙函数
  * 参    数:无
  * 返 回 值:无
  * 注意事项:无
  */
void W25Q64_WaitBusy(void)
{
    MySPI_Start();//起始
    MySPI_WarpByte(W25Q64_READ_STATUS_REGISTER_1);  //发送
    
    uint32_t TimeOut = 100000;
    while((MySPI_WarpByte(W25Q64_DUMMY_BYTE)&0x01) == 0x01)    //读取状态寄存器1的Busy位是否为1,为1则等待
    {
        TimeOut--;
        if(TimeOut == 0)
        {
            break;  //超时退出
        }
    }
    MySPI_Stop();   //停止
}

/**
  * 函    数:页编程
  * 参    数:Address       要写入那个页地址
              *DataArray    存储字节所用的数组
              Count         一次写入多少字节
  * 返 回 值:无
  * 注意事项:一次只能写入最多0-256个字节
  */
void W25Q64_PageProgram(uint32_t Address, uint8_t* DataArray,uint16_t Count)//(0-256,所以要16位)
{
    W25Q64_WaitBusy();
    //事前等待。(事后等待是先等待再退出,比较保险。 事前等待可以先做别的事,再进去。效率高)
    
    W25Q64_WriteEnable();//写使能(每次写时都要先写使能)
    
    MySPI_Start();//起始
    MySPI_WarpByte(W25Q64_PAGE_PROGRAM);//发送页编程指令
    MySPI_WarpByte(Address >> 16 );
    MySPI_WarpByte(Address >> 8 );//(接收只能接收8位。会自动舍弃)
    MySPI_WarpByte(Address >> 0);//发送页地址
    uint16_t i = 0;
    for(i = 0; i < Count; i++)
    {
        MySPI_WarpByte(DataArray[i]);//发送Count个数组的第i位
    }
    MySPI_Stop();//停止
}

/**
  * 函    数:页擦除
  * 参    数:Address       要擦除那一页
  * 返 回 值:无
  * 注意事项:最小的擦除单位。4kb 1扇区
  */
void W25Q64_PageErase(uint32_t Address)
{
    W25Q64_WaitBusy();
    //事前等待。(事后等待是先等待再退出,比较保险。 事前等待可以先做别的事,再进去。效率高)
    
    W25Q64_WriteEnable();//写使能(每次写时都要先写使能)
    
    MySPI_Start();//起始
    MySPI_WarpByte(W25Q64_SECTOR_ERASE_4KB);//发送页编程指令
    MySPI_WarpByte(Address >> 16 );
    MySPI_WarpByte(Address >> 8 );//发送地址
    MySPI_WarpByte(Address >> 0);
    MySPI_Stop();//停止
}

/**
  * 函    数:读取数据
  * 参    数:Address       要读取个地址
              *DataArray    存储字节所用的数组
              Count         一次读取多少字节
  * 返 回 值:无
  * 注意事项:读取可以无限制读取
  */
void W25Q64_ReadData(uint32_t Address, uint8_t* DataArray,uint32_t Count)//读取没有限制
{
    W25Q64_WaitBusy();
    //事前等待。(事后等待是先等待再退出,比较保险。 事前等待可以先做别的事,再进去。效率高)
    
    MySPI_Start();//起始
    MySPI_WarpByte(W25Q64_READ_DATA);//发送页编程指令
    MySPI_WarpByte(Address >> 16 );
    MySPI_WarpByte(Address >> 8 );//(接收只能接收8位。会自动舍弃)
    MySPI_WarpByte(Address >> 0);//发送页地址
    uint32_t i = 0;
    for(i = 0; i < Count; i++)
    {
        DataArray[i] = MySPI_WarpByte(W25Q64_DUMMY_BYTE);//接收Count个字节,放到数组的第i位
    }
    MySPI_Stop();//停止
}

W25Q64.h

#ifndef __W25Q64_H
#define __W25Q64_H

//初始化W25Q64
void W25Q64_Init(void);

/*厂商和设备ID号*/
typedef struct ID
{
    uint8_t MID;//8位厂商ID
    uint16_t DID;//16位设备ID
}ID;
extern ID W25Q64_ID;

//获取厂商和设备号ID
void W25Q64_ReadID(ID* Str);
//页编程
void W25Q64_PageProgram(uint32_t Address, uint8_t* DataArray,uint16_t Count);
//页擦除
void W25Q64_PageErase(uint32_t Address);
//读取
void W25Q64_ReadData(uint32_t Address, uint8_t* DataArray,uint32_t Count);
#endif

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

main.c

#include "stm32f10x.h"                  // Device header
#include "oled.h"
#include "Delay.h"
#include "key.h"
#include "W25Q64.h"

/**
  * 函    数:验证SPI控制W25Q64存储器
  * 参    数:无
  * 返 回 值:无
  * 注意事项:无
  */

uint8_t ArrWrite[] = {0x01,0x02,0x03,0x04};
uint8_t ArrRead[4];

int main()
{
    Delay_Init();//初始化演示
    OLED_Init();//初始化OLED;
    W25Q64_Init();//初始化W25Q64存储器

    OLED_ShowString(1,1,"MID:  ,DID:   ");
    
    W25Q64_ReadID(&W25Q64_ID);//读ID放到这个结构体中
    OLED_ShowHexNum(1,5,W25Q64_ID.MID,2);
    OLED_ShowHexNum(1,12,W25Q64_ID.DID,4);//显示MID DID
    
    OLED_ShowString(2,1,"W:");
    OLED_ShowString(3,1,"R:");
    
    W25Q64_PageErase(0x000000);               //擦除地址。写入前需要(最好定位到扇区的起始地址(后三位为0))
    W25Q64_PageProgram(0x000000,ArrWrite,4);  //写入数组中数据到存储器
    W25Q64_ReadData(0x000000,ArrRead,4);      //读取存储器中数据到数组
    
    OLED_ShowHexNum(2, 3, ArrWrite[0], 2);
    OLED_ShowHexNum(2 ,6, ArrWrite[1], 2);
    OLED_ShowHexNum(2, 9, ArrWrite[2], 2);
    OLED_ShowHexNum(2, 12, ArrWrite[3], 2);
    
    OLED_ShowHexNum(3, 3, ArrRead[0], 2);
    OLED_ShowHexNum(3 ,6, ArrRead[1], 2);
    OLED_ShowHexNum(3, 9, ArrRead[2], 2);
    OLED_ShowHexNum(3, 12, ArrRead[3], 2);

    while(1)
    {

    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2058547.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

网安新声 | 从微软“狂躁许可”漏洞事件看安全新挑战与应对策略

网安加社区【网安新声】栏目&#xff0c;汇聚网络安全领域的权威专家与资深学者&#xff0c;紧跟当下热点安全事件、剖析前沿技术动态及政策导向&#xff0c;以专业视野和前瞻洞察&#xff0c;引领行业共同探讨并应对新挑战的策略与可行路径。 近期&#xff0c;微软披露了一个最…

AIGC企业知识库系统的全方位应用

在知识爆炸的时代&#xff0c;企业如同航行在浩瀚信息海洋中的巨轮&#xff0c;每一滴知识的浪花都可能成为推动其破浪前行的动力。而 AIGC企业知识库系统可以帮助企业精准捕捞、高效利用这些宝贵资源&#xff0c;不仅重塑了企业知识管理的版图&#xff0c;更引领了一场前所未有…

秋招力扣Hot100刷题总结——回溯

回溯问题通常应用于解决排列组合等问题&#xff0c;需要注意的是回溯函数中的参数、结束条件、遍历开始顺序等。 回溯三部曲&#xff1a; &#xff08;1&#xff09;确定递归函数的参数。 &#xff08;2&#xff09;确定递归函数的终止条件。 &#xff08;3&#xff09;确定单层…

错误:Input string was not in a correct format.

之前写的桌面端&#xff0c;在国内客户电脑运行着没问题&#xff0c;到欧洲国家电脑上就挂掉了 原因&#xff1a;TM 小数点不是. 而是, 是逗号&#xff0c;不明觉厉 解决办法&#xff1a; 1、更改客户电脑配置 这里把逗号改成.就行了 但是这种办法比较笨&#xff0c;总不能…

视频检索技术为电子商务直播领域带来了前所未有的革新

视频检测在这个场景中指的是通过视频流实时识别和检索直播中销售人员展示的商品。这涉及到从连续的视频帧中分析和识别商品的视觉内容&#xff0c;通常与语音和文本数据结合&#xff0c;以提高识别准确性。 技术原理 文本引导的注意机制&#xff1a;这一机制通过直播中销售人员…

初始redis:List

列表 List 相当于数组或者顺序表。 对于List来说&#xff0c;两侧都可以插入和删除&#xff0c;时间复杂度是O(1)。 有很多的操作&#xff0c;比如 llen 可以获取List的长度&#xff0c;lrem 可以删除元素 &#xff0c;lrange可以去一个字符串 &#xff0c; lindex可以根据下标…

MBR20100FCT-ASEMI无人机专用MBR20100FCT

编辑&#xff1a;ll MBR20100FCT-ASEMI无人机专用MBR20100FCT 型号&#xff1a;MBR20100FCT 品牌&#xff1a;ASEMI 封装&#xff1a;TO-220F 批号&#xff1a;最新 恢复时间&#xff1a;35ns 最大平均正向电流&#xff08;IF&#xff09;&#xff1a;20A 最大循环峰值…

Leetcode344. 反转字符串(双指针-对撞)

题目描述&#xff1a; 编写一个函数&#xff0c;其作用是将输入的字符串反转过来。输入字符串以字符数组 s 的形式给出。 不要给另外的数组分配额外的空间&#xff0c;你必须原地修改输入数组、使用 O(1) 的额外空间解决这一问题。 示例&#xff1a; 示例 1&#xff1a; 输…

比铁饭碗还好的专业,未来人才缺口超大,而且就业压力还小!

高考是许多学生心中的一件大事&#xff0c;高考成绩的好坏&#xff0c;直接决定着进入什么样的大学&#xff0c;或者选择什么样的专业。**而且在当今这个日新月异的时代&#xff0c;选择一个既有发展前景又相对稳定的职业成为了许多学生和家长的关注焦点。**其实我国有部分大学…

Python爬虫——简单网页抓取(实战案例)小白篇

Python 爬虫是一种强大的工具&#xff0c;用于从网页中提取数据。这里&#xff0c;我将通过一个简单的实战案例来展示如何使用 Python 和一些流行的库&#xff08;如 requests 和 BeautifulSoup&#xff09;来抓取网页数据。 实战案例&#xff1a;抓取一个新闻网站的头条新闻标…

UIAbility组件的启动模式

UIAbility的启动模式是指UIAbility实例在启动时的不同呈现状态。针对不同的业务场景&#xff0c;系统提供了三种启动模式&#xff1a; 1、singleton启动模式 单实例模式&#xff0c;也是默认情况下的启动模式。系统中只存在唯一一个该UIAbility实例&#xff0c;即在最近任务列…

BMS杂谈1

1、LTC凌特和ADI是一家 凌力尔特&#xff08;Linear Technology&#xff09;是一家模拟芯片公司&#xff0c;成立于1981年&#xff0c;由‌Bob Swanson和‌Bob Dobkin创立&#xff0c;总部位于硅谷。2016年&#xff0c;凌力尔特被ADI公司以约150亿美元的价格收购。收购完成后&a…

如何免费获取乡镇级边界数据geoJson数据

如何免费获取乡镇级边界数据geoJson数据 我们可以通过 阿里云数据可视化平台 &#xff0c;可以获取到中国各个省份/区级/县级的json数据&#xff0c;但是区级和县级&#xff0c;并没有包含街道和乡镇的数据 获取乡镇级边界数据 1.下载bigemap全能版 安装好后选择你要导出的…

数据链路层 III(介质访问控制)【★★★★★】

&#xff08;★★&#xff09;代表非常重要的知识点&#xff0c;&#xff08;★&#xff09;代表重要的知识点。 介质访问控制所要完成的主要任务是&#xff1a;为使用介质的每个结点隔离来自同一信道上其他结点所传送的信号&#xff0c;以协调活动结点的传输。 下图所示是广播…

实测可用,ComfyUI工作流:个性二维码设计

前言 ComfyUI工作流&#xff1a;个性二维码设计 朋友们&#xff0c;还在花钱去找别人设计二维码吗&#xff1f; 其实只需要一个comfyui工作流就能解决了。 你是否想过&#xff0c;通过简单的文本描述就能生成惊艳的视觉效果&#xff1f;又或者&#xff0c;你是否想过将二维码…

[数据集][目标检测]道路积水检测数据集VOC+YOLO格式2699张1类别

数据集格式&#xff1a;Pascal VOC格式YOLO格式(不包含分割路径的txt文件&#xff0c;仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数)&#xff1a;2699 标注数量(xml文件个数)&#xff1a;2699 标注数量(txt文件个数)&#xff1a;2699 标注…

Leetcode Java学习记录——动态规划基础

概念 首先想到递归、分治。动态规划本质也一样。 共性&#xff1a;找到重复子问题 差异性&#xff1a;有最优子结构&#xff0c;中途可以淘汰次优解。 动态规划是分治最优子结构。 例题 斐波那契数列 递归实现&#xff0c;时间复杂度是指数级。 最基础的写法为 int fib(i…

LCP 633 平方数之和 [leetcode - 8]

最近是在研究双指针啊&#xff0c;leetcode刷的题都是这方面的。都记录在最近的文章里&#xff0c;大家有兴趣可以去我主页看看 LCP633 平方数之和 给定一个非负整数 c &#xff0c;你要判断是否存在两个整数 a 和 b&#xff0c;使得 a2 b2 c 。 示例 1&#xff1a; 输入&…

工业相机错峰启动优化方案

为了解决9台工业相机的错峰启动问题&#xff0c;可以通过LabVIEW程序来实现相机启动的优化。这样可以减少USB带宽的瞬间占用&#xff0c;并避免多个相机同时启动导致的资源冲突。下面是实现错峰启动的思路&#xff1a; 1. 分析启动顺序: 分组管理: 将9台相机分为多个组&#xf…

【Datawhale X 魔搭 】AI夏令营第四期大模型方向,Task4:源大模型微调实战(持续更新)

1.1 大模型微调技术简介 模型微调也被称为指令微调&#xff08;Instruction Tuning&#xff09;或者有监督微调&#xff08;Supervised Fine-tuning, SFT&#xff09;&#xff0c;该方法利用成对的任务输入与预期输出数据&#xff0c;训练模型学会以问答的形式解答问题&#xf…