Clion开发Stm32之存储模块(W25Q64)驱动编写

前言

涵盖之前文章:

  1. Clion开发STM32之HAL库SPI封装(基础库)

W25Q64驱动

头文件

c 复制代码
#ifndef F1XX_TEMPLATE_MODULE_W25Q64_H
#define F1XX_TEMPLATE_MODULE_W25Q64_H

#include "sys_core.h"
/* Private typedef -----------------------------------------------------------*/
//#define  sFLASH_ID                       0xEF3015     //W25X16
//#define  sFLASH_ID                       0xEF4015	    //W25Q16
#define  sFLASH_ID                        0XEF4017     //W25Q64
//#define  sFLASH_ID                       0XEF4018    //W25Q128
//#define SPI_FLASH_PageSize            4096
#define SPI_FLASH_PageSize              256
#define SPI_FLASH_PerWritePageSize      256

/* Private define ------------------------------------------------------------*/
/*命令定义-开头*******************************/
#define W25X_WriteEnable              0x06
#define W25X_WriteDisable              0x04
#define W25X_ReadStatusReg            0x05
#define W25X_WriteStatusReg            0x01
#define W25X_ReadData                    0x03
#define W25X_FastReadData              0x0B
#define W25X_FastReadDual              0x3B
#define W25X_PageProgram              0x02
#define W25X_BlockErase                  0xD8
#define W25X_SectorErase              0x20
#define W25X_ChipErase                  0xC7
#define W25X_PowerDown                  0xB9
#define W25X_ReleasePowerDown        0xAB
#define W25X_DeviceID                    0xAB
#define W25X_ManufactDeviceID    0x90
#define W25X_JedecDeviceID            0x9F
#define WIP_Flag                  0x01  /* Write In Progress (WIP) flag */
#define Dummy_Byte                0xFF
/*等待超时时间*/
#define SPIT_FLAG_TIMEOUT         ((uint32_t)0x5000)
#define SPIT_LONG_TIMEOUT         ((uint32_t)(10 * SPIT_FLAG_TIMEOUT))

/**
 * @memberof driver_init 驱动初始化
 * @memberof cs_low 使能低
 * @memberof cs_high 使能高
 * @memberof send_and_rec 发送并接收
 */
typedef struct {
    void (*driver_init)(void);

    void (*cs_low)(void);

    void (*cs_high)(void);

    uint8_t (*send_and_rec)(uint8_t dat);


} W25Q64_cnf_t;

void W25Q64_cnf_set(W25Q64_cnf_t *cnf);

bool W25Q64_Init(void);

void SPI_FLASH_SectorErase(uint32_t SectorAddr);

void SPI_FLASH_BulkErase(void);

void SPI_FLASH_PageWrite(uint8_t *pBuffer, uint32_t WriteAddr, uint16_t NumByteToWrite);

void SPI_FLASH_BufferWrite(uint8_t *pBuffer, uint32_t WriteAddr, uint16_t NumByteToWrite);

void SPI_FLASH_BufferRead(uint8_t *pBuffer, uint32_t ReadAddr, uint16_t NumByteToRead);

uint32_t SPI_FLASH_ReadID(void);

uint32_t SPI_FLASH_ReadDeviceID(void);

void SPI_FLASH_StartReadSequence(uint32_t ReadAddr);

void SPI_Flash_PowerDown(void);

void SPI_Flash_WAKEUP(void);

uint8_t SPI_FLASH_ReadByte(void);

uint8_t SPI_FLASH_SendByte(uint8_t byte);

void SPI_FLASH_WriteEnable(void);

void SPI_FLASH_WaitForWriteEnd(void);

uint16_t SPI_TIMEOUT_UserCallback(uint8_t errorCode);

#endif //F1XX_TEMPLATE_MODULE_W25Q64_H

源文件

c 复制代码
#include "w25q64/module_w25q64.h"

#define DBG_ENABLE
#define DBG_SECTION_NAME "w25q64"
#define DBG_LEVEL DBG_LOG // DBG_LOG DBG_INFO DBG_WARNING DBG_ERROR

#include "sys_dbg.h"

static W25Q64_cnf_t *cnf_ptr = NULL;
#define SPI_FLASH_CS_LOW cnf_ptr->cs_low
#define SPI_FLASH_CS_HIGH cnf_ptr->cs_high
static volatile uint32_t SPITimeout = SPIT_LONG_TIMEOUT;

void W25Q64_cnf_set(W25Q64_cnf_t *cnf) {
    cnf_ptr = cnf;
}

bool W25Q64_Init(void) {
    if (cnf_ptr == NULL) return false;
    cnf_ptr->driver_init();/*驱动初始化*/
    SPI_Flash_WAKEUP();/*唤醒*/
    SPI_FLASH_ReadDeviceID();
    if (SPI_FLASH_ReadID() == sFLASH_ID) {
        return true;
    }
    return false;
}


/**
 * @brief  擦除FLASH扇区
 * @param  SectorAddr:要擦除的扇区地址
 * @retval 无
 */
void SPI_FLASH_SectorErase(uint32_t SectorAddr) {
    /* 发送FLASH写使能命令 */
    SPI_FLASH_WriteEnable();
    SPI_FLASH_WaitForWriteEnd();
    /* 擦除扇区 */
    /* 选择FLASH: CS低电平 */
    SPI_FLASH_CS_LOW();
    /* 发送扇区擦除指令*/
    SPI_FLASH_SendByte(W25X_SectorErase);
    /*发送擦除扇区地址的高位*/
    SPI_FLASH_SendByte((SectorAddr & 0xFF0000) >> 16);
    /* 发送擦除扇区地址的中位 */
    SPI_FLASH_SendByte((SectorAddr & 0xFF00) >> 8);
    /* 发送擦除扇区地址的低位 */
    SPI_FLASH_SendByte(SectorAddr & 0xFF);
    /* 停止信号 FLASH: CS 高电平 */
    SPI_FLASH_CS_HIGH();
    /* 等待擦除完毕*/
    SPI_FLASH_WaitForWriteEnd();
}


/**
 * @brief  擦除FLASH扇区,整片擦除
 * @param  无
 * @retval 无
 */
void SPI_FLASH_BulkErase(void) {
    /* 发送FLASH写使能命令 */
    SPI_FLASH_WriteEnable();

    /* 整块 Erase */
    /* 选择FLASH: CS低电平 */
    SPI_FLASH_CS_LOW();
    /* 发送整块擦除指令*/
    SPI_FLASH_SendByte(W25X_ChipErase);
    /* 停止信号 FLASH: CS 高电平 */
    SPI_FLASH_CS_HIGH();

    /* 等待擦除完毕*/
    SPI_FLASH_WaitForWriteEnd();
}

/**
 * @brief  对FLASH按页写入数据,调用本函数写入数据前需要先擦除扇区
 * @param	pBuffer,要写入数据的指针
 * @param WriteAddr,写入地址
 * @param  NumByteToWrite,写入数据长度,必须小于等于SPI_FLASH_PerWritePageSize
 * @retval 无
 */
void SPI_FLASH_PageWrite(uint8_t *pBuffer, uint32_t WriteAddr, uint16_t NumByteToWrite) {
    /* 发送FLASH写使能命令 */
    SPI_FLASH_WriteEnable();

    /* 选择FLASH: CS低电平 */
    SPI_FLASH_CS_LOW();
    /* 写页写指令*/
    SPI_FLASH_SendByte(W25X_PageProgram);
    /*发送写地址的高位*/
    SPI_FLASH_SendByte((WriteAddr & 0xFF0000) >> 16);
    /*发送写地址的中位*/
    SPI_FLASH_SendByte((WriteAddr & 0xFF00) >> 8);
    /*发送写地址的低位*/
    SPI_FLASH_SendByte(WriteAddr & 0xFF);

    if (NumByteToWrite > SPI_FLASH_PerWritePageSize) {
        NumByteToWrite = SPI_FLASH_PerWritePageSize;
        LOG_E("SPI_FLASH_PageWrite too large!");
    }

    /* 写入数据*/
    while (NumByteToWrite--) {
        /* 发送当前要写入的字节数据 */
        SPI_FLASH_SendByte(*pBuffer);
        /* 指向下一字节数据 */
        pBuffer++;
    }

    /* 停止信号 FLASH: CS 高电平 */
    SPI_FLASH_CS_HIGH();

    /* 等待写入完毕*/
    SPI_FLASH_WaitForWriteEnd();
}


/**
 * @brief  对FLASH写入数据,调用本函数写入数据前需要先擦除扇区
 * @param	pBuffer,要写入数据的指针
 * @param  WriteAddr,写入地址
 * @param  NumByteToWrite,写入数据长度
 * @retval 无
 */
void SPI_FLASH_BufferWrite(uint8_t *pBuffer, uint32_t WriteAddr, uint16_t NumByteToWrite) {
    uint8_t NumOfPage = 0, NumOfSingle = 0, Addr = 0, count = 0, temp = 0;

    /*mod运算求余,若writeAddr是SPI_FLASH_PageSize整数倍,运算结果Addr值为0*/
    Addr = WriteAddr % SPI_FLASH_PageSize;

    /*差count个数据值,刚好可以对齐到页地址*/
    count = SPI_FLASH_PageSize - Addr;
    /*计算出要写多少整数页*/
    NumOfPage = NumByteToWrite / SPI_FLASH_PageSize;
    /*mod运算求余,计算出剩余不满一页的字节数*/
    NumOfSingle = NumByteToWrite % SPI_FLASH_PageSize;

    /* Addr=0,则WriteAddr 刚好按页对齐 aligned  */
    if (Addr == 0) {
        /* NumByteToWrite < SPI_FLASH_PageSize */
        if (NumOfPage == 0) {
            SPI_FLASH_PageWrite(pBuffer, WriteAddr, NumByteToWrite);
        } else /* NumByteToWrite > SPI_FLASH_PageSize */
        {
            /*先把整数页都写了*/
            while (NumOfPage--) {
                SPI_FLASH_PageWrite(pBuffer, WriteAddr, SPI_FLASH_PageSize);
                WriteAddr += SPI_FLASH_PageSize;
                pBuffer += SPI_FLASH_PageSize;
            }

            /*若有多余的不满一页的数据,把它写完*/
            SPI_FLASH_PageWrite(pBuffer, WriteAddr, NumOfSingle);
        }
    }
        /* 若地址与 SPI_FLASH_PageSize 不对齐  */
    else {
        /* NumByteToWrite < SPI_FLASH_PageSize */
        if (NumOfPage == 0) {
            /*当前页剩余的count个位置比NumOfSingle小,写不完*/
            if (NumOfSingle > count) {
                temp = NumOfSingle - count;

                /*先写满当前页*/
                SPI_FLASH_PageWrite(pBuffer, WriteAddr, count);
                WriteAddr += count;
                pBuffer += count;

                /*再写剩余的数据*/
                SPI_FLASH_PageWrite(pBuffer, WriteAddr, temp);
            } else /*当前页剩余的count个位置能写完NumOfSingle个数据*/
            {
                SPI_FLASH_PageWrite(pBuffer, WriteAddr, NumByteToWrite);
            }
        } else /* NumByteToWrite > SPI_FLASH_PageSize */
        {
            /*地址不对齐多出的count分开处理,不加入这个运算*/
            NumByteToWrite -= count;
            NumOfPage = NumByteToWrite / SPI_FLASH_PageSize;
            NumOfSingle = NumByteToWrite % SPI_FLASH_PageSize;

            SPI_FLASH_PageWrite(pBuffer, WriteAddr, count);
            WriteAddr += count;
            pBuffer += count;

            /*把整数页都写了*/
            while (NumOfPage--) {
                SPI_FLASH_PageWrite(pBuffer, WriteAddr, SPI_FLASH_PageSize);
                WriteAddr += SPI_FLASH_PageSize;
                pBuffer += SPI_FLASH_PageSize;
            }
            /*若有多余的不满一页的数据,把它写完*/
            if (NumOfSingle != 0) {
                SPI_FLASH_PageWrite(pBuffer, WriteAddr, NumOfSingle);
            }
        }
    }
}

/**
 * @brief  读取FLASH数据
 * @param 	pBuffer,存储读出数据的指针
 * @param   ReadAddr,读取地址
 * @param   NumByteToRead,读取数据长度
 * @retval 无
 */
void SPI_FLASH_BufferRead(uint8_t *pBuffer, uint32_t ReadAddr, uint16_t NumByteToRead) {
    /* 选择FLASH: CS低电平 */
    SPI_FLASH_CS_LOW();

    /* 发送 读 指令 */
    SPI_FLASH_SendByte(W25X_ReadData);

    /* 发送 读 地址高位 */
    SPI_FLASH_SendByte((ReadAddr & 0xFF0000) >> 16);
    /* 发送 读 地址中位 */
    SPI_FLASH_SendByte((ReadAddr & 0xFF00) >> 8);
    /* 发送 读 地址低位 */
    SPI_FLASH_SendByte(ReadAddr & 0xFF);

    /* 读取数据 */
    while (NumByteToRead--) {
        /* 读取一个字节*/
        *pBuffer = SPI_FLASH_SendByte(Dummy_Byte);
        /* 指向下一个字节缓冲区 */
        pBuffer++;
    }

    /* 停止信号 FLASH: CS 高电平 */
    SPI_FLASH_CS_HIGH();
}


/**
 * @brief  读取FLASH ID
 * @param 	无
 * @retval FLASH ID
 */
uint32_t SPI_FLASH_ReadID(void) {
    uint32_t Temp = 0, Temp0 = 0, Temp1 = 0, Temp2 = 0;

    /* 开始通讯:CS低电平 */
    SPI_FLASH_CS_LOW();

    /* 发送JEDEC指令,读取ID */
    SPI_FLASH_SendByte(W25X_JedecDeviceID);

    /* 读取一个字节数据 */
    Temp0 = SPI_FLASH_SendByte(Dummy_Byte);

    /* 读取一个字节数据 */
    Temp1 = SPI_FLASH_SendByte(Dummy_Byte);

    /* 读取一个字节数据 */
    Temp2 = SPI_FLASH_SendByte(Dummy_Byte);

    /* 停止通讯:CS高电平 */
    SPI_FLASH_CS_HIGH();

    /*把数据组合起来,作为函数的返回值*/
    Temp = (Temp0 << 16) | (Temp1 << 8) | Temp2;

    return Temp;
}

/**
 * @brief  读取FLASH Device ID
 * @param 	无
 * @retval FLASH Device ID
 */
uint32_t SPI_FLASH_ReadDeviceID(void) {
    uint32_t Temp = 0;

    /* Select the FLASH: Chip Select low */
    SPI_FLASH_CS_LOW();

    /* Send "RDID " instruction */
    SPI_FLASH_SendByte(W25X_DeviceID);
    SPI_FLASH_SendByte(Dummy_Byte);
    SPI_FLASH_SendByte(Dummy_Byte);
    SPI_FLASH_SendByte(Dummy_Byte);

    /* Read a byte from the FLASH */
    Temp = SPI_FLASH_SendByte(Dummy_Byte);

    /* Deselect the FLASH: Chip Select high */
    SPI_FLASH_CS_HIGH();

    return Temp;
}

/*******************************************************************************
* Function Name  : SPI_FLASH_StartReadSequence
* Description    : Initiates a read data byte (READ) sequence from the Flash.
*                  This is done by driving the /CS line low to select the device,
*                  then the READ instruction is transmitted followed by 3 bytes
*                  address. This function exit and keep the /CS line low, so the
*                  Flash still being selected. With this technique the whole
*                  content of the Flash is read with a single READ instruction.
* Input          : - ReadAddr : FLASH's internal address to read from.
* Output         : None
* Return         : None
*******************************************************************************/
void SPI_FLASH_StartReadSequence(uint32_t ReadAddr) {
    /* Select the FLASH: Chip Select low */
    SPI_FLASH_CS_LOW();

    /* Send "Read from Memory " instruction */
    SPI_FLASH_SendByte(W25X_ReadData);

    /* Send the 24-bit address of the address to read from -----------------------*/
    /* Send ReadAddr high nibble address byte */
    SPI_FLASH_SendByte((ReadAddr & 0xFF0000) >> 16);
    /* Send ReadAddr medium nibble address byte */
    SPI_FLASH_SendByte((ReadAddr & 0xFF00) >> 8);
    /* Send ReadAddr low nibble address byte */
    SPI_FLASH_SendByte(ReadAddr & 0xFF);
}


/**
 * @brief  使用SPI读取一个字节的数据
 * @param  无
 * @retval 返回接收到的数据
 */
uint8_t SPI_FLASH_ReadByte(void) {
    return (SPI_FLASH_SendByte(Dummy_Byte));
}


/**
 * @brief  使用SPI发送一个字节的数据
 * @param  byte:要发送的数据
 * @retval 返回接收到的数据
 */
uint8_t SPI_FLASH_SendByte(uint8_t byte) {
    return cnf_ptr->send_and_rec(byte);
//    SPITimeout = SPIT_FLAG_TIMEOUT;
//
//    /* 等待发送缓冲区为空,TXE事件 */
//    while (__HAL_SPI_GET_FLAG(&SpiHandle, SPI_FLAG_TXE) == RESET) {
//        if ((SPITimeout--) == 0) return SPI_TIMEOUT_UserCallback(0);
//    }
//
//    /* 写入数据寄存器,把要写入的数据写入发送缓冲区 */
//    WRITE_REG(SpiHandle.Instance->DR, byte);
//
//    SPITimeout = SPIT_FLAG_TIMEOUT;
//
//    /* 等待接收缓冲区非空,RXNE事件 */
//    while (__HAL_SPI_GET_FLAG(&SpiHandle, SPI_FLAG_RXNE) == RESET) {
//        if ((SPITimeout--) == 0) return SPI_TIMEOUT_UserCallback(1);
//    }
//
//    /* 读取数据寄存器,获取接收缓冲区数据 */
//    return READ_REG(SpiHandle.Instance->DR);
}


/**
 * @brief  向FLASH发送 写使能 命令
 * @param  none
 * @retval none
 */
void SPI_FLASH_WriteEnable(void) {
    /* 通讯开始:CS低 */
    SPI_FLASH_CS_LOW();

    /* 发送写使能命令*/
    SPI_FLASH_SendByte(W25X_WriteEnable);

    /*通讯结束:CS高 */
    SPI_FLASH_CS_HIGH();
}

/**
 * @brief  等待WIP(BUSY)标志被置0,即等待到FLASH内部数据写入完毕
 * @param  none
 * @retval none
 */
void SPI_FLASH_WaitForWriteEnd(void) {
    uint8_t FLASH_Status = 0;

    /* 选择 FLASH: CS 低 */
    SPI_FLASH_CS_LOW();

    /* 发送 读状态寄存器 命令 */
    SPI_FLASH_SendByte(W25X_ReadStatusReg);

    SPITimeout = SPIT_FLAG_TIMEOUT;
    /* 若FLASH忙碌,则等待 */
    do {
        /* 读取FLASH芯片的状态寄存器 */
        FLASH_Status = SPI_FLASH_SendByte(Dummy_Byte);

        {
            if ((SPITimeout--) == 0) {
                SPI_TIMEOUT_UserCallback(4);
                return;
            }
        }
    } while ((FLASH_Status & WIP_Flag) == 1); /* 正在写入标志 */

    /* 停止信号  FLASH: CS 高 */
    SPI_FLASH_CS_HIGH();
}


//进入掉电模式
void SPI_Flash_PowerDown(void) {
    /* 选择 FLASH: CS 低 */
    SPI_FLASH_CS_LOW();

    /* 发送 掉电 命令 */
    SPI_FLASH_SendByte(W25X_PowerDown);

    /* 停止信号  FLASH: CS 高 */
    SPI_FLASH_CS_HIGH();
}

//唤醒
void SPI_Flash_WAKEUP(void) {
    /*选择 FLASH: CS 低 */
    SPI_FLASH_CS_LOW();

    /* 发上 上电 命令 */
    SPI_FLASH_SendByte(W25X_ReleasePowerDown);

    /* 停止信号 FLASH: CS 高 */
    SPI_FLASH_CS_HIGH();                   //等待TRES1
}


/**
  * @brief  等待超时回调函数
  * @param  None.
  * @retval None.
  */
uint16_t SPI_TIMEOUT_UserCallback(uint8_t errorCode) {
    /* 等待超时后的处理,输出错误信息 */
    LOG_E("%s!errorCode = %d", FILENAME_, errorCode);
    return 0;
}

测试配置

c 复制代码
#include "app_conf.h"

#define APP_CONF_ENABLE_W25Q64_CNF (1)
#if APP_CONF_ENABLE_W25Q64_CNF

#include "w25q64/module_w25q64.h"

#define DBG_ENABLE
#define DBG_SECTION_NAME "W25Q64_CNF"
#define DBG_LEVEL DBG_LOG // DBG_LOG DBG_INFO DBG_WARNING DBG_ERROR

#include "sys_dbg.h"
#include "stdio.h"

#define  FLASH_WriteAddress     0x00000
#define  FLASH_ReadAddress      FLASH_WriteAddress
#define  FLASH_SectorToErase    FLASH_WriteAddress
/* 获取缓冲区的长度 */
#define countof(a)      (sizeof(a) / sizeof(*(a)))
#define  BufferSize (countof(Tx_Buffer)-1)

uint8_t Tx_Buffer[] = "asdafwer ijfsifhnsow das";
uint8_t Rx_Buffer[BufferSize];
__IO uint32_t FlashID = 0;
/*-********************************************W25Q64_CNF变量定义******************************************-*/
static stm_pin_define_t *w25q64_cs = NULL;
static SPI_HandleTypeDef *w25q64_spi = NULL;
static W25Q64_cnf_t w25q64_cnf;

static void w25q64_cs_high(void) { stm32_pin_define_high(w25q64_cs); }

static void w25q64_cs_low(void) { stm32_pin_define_low(w25q64_cs); }

static void w25q64_driver_init(void);

static uint8_t w25q64_send(uint8_t dat);

/*-********************************************W25Q64_CNF_pre_init******************************************-*/
static void W25Q64_CNF_pre_init() {
    w25q64_cs = stm_get_pin(PC0);
    w25q64_spi = handle_get_by_id(spi1_id);/*这里可以换成自定义spi句柄*/

    w25q64_cnf.cs_high = w25q64_cs_high;
    w25q64_cnf.cs_low = w25q64_cs_low;
    w25q64_cnf.send_and_rec = w25q64_send;
    w25q64_cnf.driver_init = w25q64_driver_init;
    W25Q64_cnf_set(&w25q64_cnf);
}

sys_pre_init_export(W25Q64_CNF, W25Q64_CNF_pre_init);


/*-********************************************W25Q64_CNF_init******************************************-*/
static void W25Q64_CNF_init() {
    if (W25Q64_Init()) {
        FlashID = sFLASH_ID;
        LOG_D("FILE: %s", FILENAME_);
    }
    float num = MIN3_(1.3f, 2.3f, 4.3f);
    LOG_D("MIN %f", num);
}

sys_init_export(W25Q64_CNF, W25Q64_CNF_init);

/*-***********************************************W25Q64_CNF_after_init***************************************-*/
static void W25Q64_CNF_after_init() {
    /* 获取 Flash Device ID */
    if (FlashID == sFLASH_ID) {
        LOG_D("检测到SPI FLASH W25Q64 !");

        /* 擦除将要写入的 SPI FLASH 扇区,FLASH写入前要先擦除 */
        SPI_FLASH_SectorErase(FLASH_SectorToErase);

        /* 将发送缓冲区的数据写到flash中 */
        SPI_FLASH_BufferWrite(Tx_Buffer, FLASH_WriteAddress, BufferSize);
        LOG_D("写入的数据为:%s", Tx_Buffer);

        /* 将刚刚写入的数据读出来放到接收缓冲区中 */
        SPI_FLASH_BufferRead(Rx_Buffer, FLASH_ReadAddress, BufferSize);
        LOG_D("读出的数据为:%s", Rx_Buffer);

        /* 检查写入的数据与读出的数据是否相等 */
        if (0 == cmp_data(Tx_Buffer, Rx_Buffer, BufferSize)) {
            LOG_D("16M串行flash(W25Q64)测试成功!");
        } else {
            LOG_D("\r\n16M串行flash(W25Q64)测试失败!\n\r");
        }
    } else {
        LOG_D("\r\n获取不到 W25Q64 ID!\n\r");
    }
    /*进入掉电模式*/
    SPI_Flash_PowerDown();
}

sys_after_init_export(W25Q64_CNF, W25Q64_CNF_after_init);

/*-**************************************W25Q64_CNF内部使用************************************************-*/
static void w25q64_driver_init(void) {
    /**模式3 CPOL:1,CPHA:1 ; 时钟空闲状态为(高电平),在第二个时钟边沿采数据(时钟上升沿采数据)*/
    bsp_SpiHandleInit(w25q64_spi, SPI_BAUDRATEPRESCALER_8, spi_mode_3);
    /*cs 配置*/
    stm32_pin_define_mode_set(w25q64_cs, pin_mode_output);
}

static uint8_t w25q64_send(uint8_t data) {

    static uint8_t readData = 0;
    HAL_StatusTypeDef status = HAL_SPI_TransmitReceive(w25q64_spi, &data, &readData, 1, HAL_MAX_DELAY);
    if (status != HAL_OK) {
        LOG_E("w25q64_send ERR:%#x", status);
        return 0;
    }
    return readData;
}

#endif //APP_CONF_ENABLE_W25Q64_CNF

结果

相关推荐
智商偏低2 小时前
单片机之helloworld
单片机·嵌入式硬件
青牛科技-Allen4 小时前
GC3910S:一款高性能双通道直流电机驱动芯片
stm32·单片机·嵌入式硬件·机器人·医疗器械·水泵、
森焱森5 小时前
无人机三轴稳定控制(2)____根据目标俯仰角,实现俯仰稳定化控制,计算出升降舵输出
c语言·单片机·算法·架构·无人机
白鱼不小白6 小时前
stm32 USART串口协议与外设(程序)——江协教程踩坑经验分享
stm32·单片机·嵌入式硬件
S,D6 小时前
MCU引脚的漏电流、灌电流、拉电流区别是什么
驱动开发·stm32·单片机·嵌入式硬件·mcu·物联网·硬件工程
芯岭技术9 小时前
PY32F002A单片机 低成本控制器解决方案,提供多种封装
单片机·嵌入式硬件
youmdt10 小时前
Arduino IDE ESP8266连接0.96寸SSD1306 IIC单色屏显示北京时间
单片机·嵌入式硬件
嘿·嘘10 小时前
第七章 STM32内部FLASH读写
stm32·单片机·嵌入式硬件
Meraki.Zhang10 小时前
【STM32实践篇】:I2C驱动编写
stm32·单片机·iic·驱动·i2c
几个几个n12 小时前
STM32-第二节-GPIO输入(按键,传感器)
单片机·嵌入式硬件