日韩一区二区三区精品,欧美疯狂xxxxbbbb牲交,热99re久久免费视精品频,人妻互换 综合,欧美激情肉欲高潮视频

歷史上的今天

今天是:2025年07月26日(星期六)

2021年07月26日 | STM32CubeMX | 30-使用硬件SPI讀寫FLASH(W25Q64)

發(fā)布者:kappa20 來源: eefocus關(guān)鍵字:STM32CubeMX  硬件SPI  讀寫FLASH  W25Q64 手機看文章 掃描二維碼
隨時隨地手機看文章

本篇詳細(xì)的記錄了如何使用STM32CubeMX配置 STM32G070RBT6 的硬件SPI外設(shè)與 SPI Flash 通信(W25Q64)。


【STM32Cube_09】重定向printf函數(shù)到串口輸出的多種方法。

4. 封裝 SPI Flash(W25Q64)的命令和底層函數(shù)

MCU 通過向 SPI Flash 發(fā)送各種命令 來讀寫 SPI Flash內(nèi)部的寄存器,所以這種裸機驅(qū)動,首先要先宏定義出需要使用的命令,然后利用 HAL 庫提供的庫函數(shù),封裝出三個底層函數(shù),便于移植:

  • 向 SPI Flash 發(fā)送數(shù)據(jù)的函數(shù)

  • 從 SPI Flash 接收數(shù)據(jù)的函數(shù)

  • 發(fā)送數(shù)據(jù)的同時讀取數(shù)據(jù)的函數(shù)

接下來開始編寫代碼~

宏定義操作命令

#define ManufactDeviceID_CMD 0x90

#define READ_STATU_REGISTER_1   0x05

#define READ_STATU_REGISTER_2   0x35

#define READ_DATA_CMD         0x03

#define WRITE_ENABLE_CMD     0x06

#define WRITE_DISABLE_CMD     0x04

#define SECTOR_ERASE_CMD     0x20

#define CHIP_ERASE_CMD         0xc7

#define PAGE_PROGRAM_CMD        0x02


封裝發(fā)送數(shù)據(jù)的函數(shù)

/**

 * @brief    SPI發(fā)送指定長度的數(shù)據(jù)

 * @param    buf  —— 發(fā)送數(shù)據(jù)緩沖區(qū)首地址

 * @param    size —— 要發(fā)送數(shù)據(jù)的字節(jié)數(shù)

 * @retval   成功返回HAL_OK

 */

static HAL_StatusTypeDef SPI_Transmit(uint8_t* send_buf, uint16_t size)

{

    return HAL_SPI_Transmit(&hspi1, send_buf, size, 100);

}


封裝接收數(shù)據(jù)的函數(shù)

/**

 * @brief   SPI接收指定長度的數(shù)據(jù)

 * @param   buf  —— 接收數(shù)據(jù)緩沖區(qū)首地址

 * @param   size —— 要接收數(shù)據(jù)的字節(jié)數(shù)

 * @retval  成功返回HAL_OK

 */

static HAL_StatusTypeDef SPI_Receive(uint8_t* recv_buf, uint16_t size)

{

   return HAL_SPI_Receive(&hspi1, recv_buf, size, 100);

}


封裝發(fā)送數(shù)據(jù)同時讀取數(shù)據(jù)的函數(shù)

/**

 * @brief   SPI在發(fā)送數(shù)據(jù)的同時接收指定長度的數(shù)據(jù)

 * @param   send_buf  —— 接收數(shù)據(jù)緩沖區(qū)首地址

 * @param   recv_buf  —— 接收數(shù)據(jù)緩沖區(qū)首地址

 * @param   size —— 要發(fā)送/接收數(shù)據(jù)的字節(jié)數(shù)

 * @retval  成功返回HAL_OK

 */

static HAL_StatusTypeDef SPI_TransmitReceive(uint8_t* send_buf, uint8_t* recv_buf, uint16_t size)

{

   return HAL_SPI_TransmitReceive(&hspi1, send_buf, recv_buf, size, 100);

}


5. 編寫W25Q64的驅(qū)動程序

接下來開始利用上一節(jié)封裝的宏定義和底層函數(shù),編寫W25Q64的驅(qū)動程序:


讀取Manufacture ID和Device ID

讀取 Flash 內(nèi)部這兩個ID有兩個作用:


檢測SPI Flash是否存在

可以根據(jù)ID判斷Flash具體型號

數(shù)據(jù)手冊上給出的操作時序如圖:

根據(jù)該時序,編寫代碼如下:


/**

 * @brief   讀取Flash內(nèi)部的ID

 * @param   none

 * @retval  成功返回device_id

 */

uint16_t W25QXX_ReadID(void)

{

    uint8_t recv_buf[2] = {0};    //recv_buf[0]存放Manufacture ID, recv_buf[1]存放Device ID

    uint16_t device_id = 0;

    uint8_t send_data[4] = {ManufactDeviceID_CMD,0x00,0x00,0x00};   //待發(fā)送數(shù)據(jù),命令+地址

    

    /* 使能片選 */

    HAL_GPIO_WritePin(W25Q64_CHIP_SELECT_PORT, W25Q64_CHIP_SELECT_PIN, GPIO_PIN_RESET);

    

    /* 發(fā)送并讀取數(shù)據(jù) */

    if (HAL_OK == SPI_Transmit(send_data, 4)) {

        if (HAL_OK == SPI_Receive(recv_buf, 2)) {

            device_id = (recv_buf[0] << 8) | recv_buf[1];

        }

    }

    

    /* 取消片選 */

    HAL_GPIO_WritePin(W25Q64_CHIP_SELECT_PORT, W25Q64_CHIP_SELECT_PIN, GPIO_PIN_SET);

    

    return device_id;

}


讀取狀態(tài)寄存器數(shù)據(jù)并判斷Flash是否忙碌

上文中提到,SPI Flash的所有操作都是靠發(fā)送命令完成的,但是 Flash 接收到命令后,需要一段時間去執(zhí)行該操作,這段時間內(nèi) Flash 處于“忙”狀態(tài),MCU 發(fā)送的命令無效,不能執(zhí)行,在 Flash 內(nèi)部有2-3個狀態(tài)寄存器,指示出 Flash 當(dāng)前的狀態(tài),有趣的一點是:


當(dāng) Flash 內(nèi)部在執(zhí)行命令時,不能再執(zhí)行 MCU 發(fā)來的命令,但是 MCU 可以一直讀取狀態(tài)寄存器,這下就很好辦了,MCU可以一直讀取,然后判斷Flash是否忙完:

mark
讀取協(xié)議如下:

根據(jù)此協(xié)議實現(xiàn)的讀取狀態(tài)寄存器的代碼如下:


/**

 * @brief     讀取W25QXX的狀態(tài)寄存器,W25Q64一共有2個狀態(tài)寄存器

 * @param     reg  —— 狀態(tài)寄存器編號(1~2)

 * @retval    狀態(tài)寄存器的值

 */

static uint8_t W25QXX_ReadSR(uint8_t reg)

{

    uint8_t result = 0; 

    uint8_t send_buf[4] = {0x00,0x00,0x00,0x00};

    switch(reg)

    {

        case 1:

            send_buf[0] = READ_STATU_REGISTER_1;

        case 2:

            send_buf[0] = READ_STATU_REGISTER_2;

        case 0:

        default:

            send_buf[0] = READ_STATU_REGISTER_1;

    }

    

     /* 使能片選 */

    HAL_GPIO_WritePin(W25Q64_CHIP_SELECT_PORT, W25Q64_CHIP_SELECT_PIN, GPIO_PIN_RESET);

    

    if (HAL_OK == SPI_Transmit(send_buf, 4)) {

        if (HAL_OK == SPI_Receive(&result, 1)) {

            HAL_GPIO_WritePin(W25Q64_CHIP_SELECT_PORT, W25Q64_CHIP_SELECT_PIN, GPIO_PIN_SET);

            

            return result;

        }

    }

    

    /* 取消片選 */

    HAL_GPIO_WritePin(W25Q64_CHIP_SELECT_PORT, W25Q64_CHIP_SELECT_PIN, GPIO_PIN_SET);


    return 0;

}


然后編寫阻塞判斷Flash是否忙碌的函數(shù):


/**

 * @brief 阻塞等待Flash處于空閑狀態(tài)

 * @param   none

 * @retval  none

 */

static void W25QXX_Wait_Busy(void)

{

    while((W25QXX_ReadSR(1) & 0x01) == 0x01); // 等待BUSY位清空

}


讀取數(shù)據(jù)

SPI Flash讀取數(shù)據(jù)可以任意地址(地址長度32bit)讀任意長度數(shù)據(jù)(最大 65535 Byte),沒有任何限制,數(shù)據(jù)手冊給出的時序如下:

根據(jù)該時序圖編寫代碼如下:


/**

 * @brief   讀取SPI FLASH數(shù)據(jù)

 * @param   buffer      —— 數(shù)據(jù)存儲區(qū)

 * @param   start_addr  —— 開始讀取的地址(最大32bit)

 * @param   nbytes      —— 要讀取的字節(jié)數(shù)(最大65535)

 * @retval  成功返回0,失敗返回-1

 */

int W25QXX_Read(uint8_t* buffer, uint32_t start_addr, uint16_t nbytes)

{

    uint8_t cmd = READ_DATA_CMD;

    

    start_addr = start_addr << 8;

    

W25QXX_Wait_Busy();

    

     /* 使能片選 */

    HAL_GPIO_WritePin(W25Q64_CHIP_SELECT_PORT, W25Q64_CHIP_SELECT_PIN, GPIO_PIN_RESET);

    

    SPI_Transmit(&cmd, 1);

    

    if (HAL_OK == SPI_Transmit((uint8_t*)&start_addr, 3)) {

        if (HAL_OK == SPI_Receive(buffer, nbytes)) {

            HAL_GPIO_WritePin(W25Q64_CHIP_SELECT_PORT, W25Q64_CHIP_SELECT_PIN, GPIO_PIN_SET);

            return 0;

        }

    }

    

    HAL_GPIO_WritePin(W25Q64_CHIP_SELECT_PORT, W25Q64_CHIP_SELECT_PIN, GPIO_PIN_SET);

    return -1;

}


寫使能/禁止

Flash 芯片默認(rèn)禁止寫數(shù)據(jù),所以在向 Flash 寫數(shù)據(jù)之前,必須發(fā)送命令開啟寫使能,數(shù)據(jù)手冊中給出的時序如下:

mark

mark

編寫函數(shù)如下:


/**

 * @brief    W25QXX寫使能,將S1寄存器的WEL置位

 * @param    none

 * @retval

 */

void W25QXX_Write_Enable(void)

{

    uint8_t cmd= WRITE_ENABLE_CMD;

    

    HAL_GPIO_WritePin(W25Q64_CHIP_SELECT_PORT, W25Q64_CHIP_SELECT_PIN, GPIO_PIN_RESET);

    

    SPI_Transmit(&cmd, 1);

    

    HAL_GPIO_WritePin(W25Q64_CHIP_SELECT_PORT, W25Q64_CHIP_SELECT_PIN, GPIO_PIN_SET);

    

    W25QXX_Wait_Busy();


}


/**

 * @brief    W25QXX寫禁止,將WEL清零

 * @param    none

 * @retval    none

 */

void W25QXX_Write_Disable(void)

{

    uint8_t cmd = WRITE_DISABLE_CMD;


    HAL_GPIO_WritePin(W25Q64_CHIP_SELECT_PORT, W25Q64_CHIP_SELECT_PIN, GPIO_PIN_RESET);

    

    SPI_Transmit(&cmd, 1);

    

    HAL_GPIO_WritePin(W25Q64_CHIP_SELECT_PORT, W25Q64_CHIP_SELECT_PIN, GPIO_PIN_SET);

    

    W25QXX_Wait_Busy();

}


擦除扇區(qū)

SPI Flash有個特性:


數(shù)據(jù)位可以由1變?yōu)?,但是不能由0變?yōu)?。


所以在向 Flash 寫數(shù)據(jù)之前,必須要先進行擦除操作,并且 Flash 最小只能擦除一個扇區(qū),擦除之后該扇區(qū)所有的數(shù)據(jù)變?yōu)?0xFF(即全為1),數(shù)據(jù)手冊中給出的時序如下:

mark

根據(jù)此時序編寫函數(shù)如下:


/**

 * @brief    W25QXX擦除一個扇區(qū)

 * @param   sector_addr    —— 扇區(qū)地址 根據(jù)實際容量設(shè)置

 * @retval  none

 * @note    阻塞操作

 */

void W25QXX_Erase_Sector(uint32_t sector_addr)

{

    uint8_t cmd = SECTOR_ERASE_CMD;

    

    sector_addr *= 4096;    //每個塊有16個扇區(qū),每個扇區(qū)的大小是4KB,需要換算為實際地址

    sector_addr <<= 8;

    

    W25QXX_Write_Enable();  //擦除操作即寫入0xFF,需要開啟寫使能

    W25QXX_Wait_Busy();        //等待寫使能完成

   

     /* 使能片選 */

    HAL_GPIO_WritePin(W25Q64_CHIP_SELECT_PORT, W25Q64_CHIP_SELECT_PIN, GPIO_PIN_RESET);

    

    SPI_Transmit(&cmd, 1);

    

    SPI_Transmit((uint8_t*)§or_addr, 3);

    

    HAL_GPIO_WritePin(W25Q64_CHIP_SELECT_PORT, W25Q64_CHIP_SELECT_PIN, GPIO_PIN_SET);

    

    W25QXX_Wait_Busy();       //等待扇區(qū)擦除完成

}


頁寫入操作

向 Flash 芯片寫數(shù)據(jù)的時候,因為 Flash 內(nèi)部的構(gòu)造,可以按頁寫入:

mark

頁寫入的時序如圖:

mark

編寫代碼如下:


/**

 * @brief    頁寫入操作

 * @param    dat —— 要寫入的數(shù)據(jù)緩沖區(qū)首地址

 * @param    WriteAddr —— 要寫入的地址

 * @param   byte_to_write —— 要寫入的字節(jié)數(shù)(0-256)

 * @retval    none

 */

void W25QXX_Page_Program(uint8_t* dat, uint32_t WriteAddr, uint16_t nbytes)

{

    uint8_t cmd = PAGE_PROGRAM_CMD;

    

    WriteAddr <<= 8;

    

    W25QXX_Write_Enable();

    

[1] [2]
關(guān)鍵字:STM32CubeMX  硬件SPI  讀寫FLASH  W25Q64 引用地址:STM32CubeMX | 30-使用硬件SPI讀寫FLASH(W25Q64)

上一篇:基于STM32芯片創(chuàng)建HelloWorld工程
下一篇:STM32CubeMX | 32-使用硬件FMC驅(qū)動TFT-LCD屏幕(MCU屏)

推薦閱讀

被譽為“Polar碼(極化碼)之父”的土耳其畢爾肯大學(xué)Erdal Arikan教授參觀華為總部,華為創(chuàng)始人任正非與其進行交流。任正非稱贊Erdal Arikan:“其研究是對人類的貢獻,華為將沿著基礎(chǔ)研究的道路前進,繼續(xù)支持教授的研究和團隊,在教授旗幟的引導(dǎo)下,為人類的發(fā)展做出貢獻。”Erdal Arikan教授表示,Polar碼的發(fā)現(xiàn)是雙方的成功,展現(xiàn)了團隊的能力和創(chuàng)...
隨著智能手機形成產(chǎn)品硬件差異化的空間越來越小,2019年以來,實現(xiàn)真正意義上的全面屏手機,成為國內(nèi)外多家終端品牌共同追求的一個目標(biāo)。作為國內(nèi)能夠提供全面屏面板的廠商之一,近日,深天馬在展開調(diào)研活動時也針對相關(guān)問題進行說明,對公司目前全面屏方案、AMOLED產(chǎn)線等情況做出回復(fù)。 此前,深天馬在武漢興建第6代LTPS AMOLED生產(chǎn)線;據(jù)悉,武漢天馬一...
  在全球工業(yè)革命、技術(shù)創(chuàng)新大背景環(huán)境影響下,我國機器人產(chǎn)業(yè)得以蓬勃發(fā)展,工業(yè)機器人應(yīng)用空間進一步擴大,服務(wù)機器人產(chǎn)品百花齊放,特種機器人智能化技術(shù)水平也在不斷提升。同時,在國家相關(guān)政策引導(dǎo)下,機器人、機器人用芯片、機器人用等產(chǎn)業(yè)園區(qū)遍地開花,整個機器人研發(fā)熱度居高不下。   然而,中國機器人產(chǎn)業(yè)70%以上的市場份額已經(jīng)被美國ABB...
北京時間7月26日下午消息,據(jù)韓聯(lián)社,韓國將為半導(dǎo)體、電池、疫苗三大國家戰(zhàn)略技術(shù)產(chǎn)業(yè)提供1.1萬億韓元(約合人民幣61.9億元)的減稅政策支持,助推核心技術(shù)產(chǎn)業(yè)不斷發(fā)展。  韓國政府26日公布涵蓋上述內(nèi)容的“2021年稅制修改案”,16項稅法修正案將于今年9月3日提交國會審議。政府介紹稱,該案的主要目標(biāo)是確保后新冠時代的經(jīng)濟增長動力、緩解K型分化、...

史海拾趣

問答坊 | AI 解惑

霹靂游俠掃描燈20模式帶拖尾C程序

/****** 霹靂游俠掃描燈20模式帶拖尾C程序,ME850調(diào)試正常*****2009/05/30*******/ #include //頭文件 rzmzy(); //紅色走馬左移聲明 rlszy(); //紅色流水左移聲明 rlsyy(); //紅色流水右移聲明 rzmyy(); //紅色走馬右移聲明 rzmnwyd(); //紅色走馬 ...…

查看全部問答∨

請大家?guī)臀铱纯催@個FPGA的頻率計的程序

接入信號發(fā)生器老不能正常顯示頻率,因此我把程序簡化再簡化。。。最后就讓信號發(fā)生器來一個高脈沖,計數(shù)器就加1,但是即使如此數(shù)碼管上的數(shù)還是亂跳。。。我都不知道是什么問題。。。數(shù)碼管顯示程序是無問題的,因為我在其他程序中用過。 我把程 ...…

查看全部問答∨

怎樣用AVR128產(chǎn)生六路PWM?

各位大俠,我通過看書只了解到如何初始化寄存器產(chǎn)生一路PWM,但由于需要用到六路PWM。但我不知道怎樣產(chǎn)生六個PWM,望各大俠高手指教,十分感激…

查看全部問答∨

在單片機系統(tǒng)里將文件保存為word格式??

單片機系統(tǒng),外設(shè)CF卡存儲器,驅(qū)動寫好了,文件系統(tǒng)FAT16,能正常讀寫。 現(xiàn)在需要把采集的數(shù)據(jù)以報表的格式生成word文檔(其中包括處理數(shù)據(jù)得到的曲線圖,就是這點最可恨)保存進CF卡里。求幫忙指點。 word文檔在磁盤里的存儲格式是什么樣的?特 ...…

查看全部問答∨

關(guān)于IP地址的問題

SOCKADDR_IN local; local.sin_family = AF_INET; local.sin_port = htons(1028); local.sin_addr.S_un.S_addr = inet_addr(ip); m_socket = socket(AF_INET,SOCK_STREAM,0); 編譯的時候出現(xiàn)下面的錯誤,請高手指點~~~~ error C2664: \'inet_ ...…

查看全部問答∨

stm32-107串口1做iap好像有問題

用例子的-c系列改的,原來是uart2該為串口1, 用超級終端看不到反應(yīng)了,改用其他串口工具,看到亂碼。 用-b系列改,能看到串口,可操作,但是燒寫flash后沒反應(yīng) 我想-b和-c因為flash不同可能有所不同,但是只發(fā)現(xiàn)pagesize和flashsize上有不 ...…

查看全部問答∨

突發(fā)奇想:提供日美器件的代換咨詢

中國有好東西的,不然兩彈一星怎么搞定的? 現(xiàn)在的民企,也有可信賴的元器件,就看我們愿意不愿意支持國產(chǎn)了。 本人愿意以后致力于這方面的工作?!?

查看全部問答∨

Beaglebone在CCSv5下Connect Target出現(xiàn)錯誤提示,但還能照常下載程序?

在把需要設(shè)置的都設(shè)置好后,最后Connect Target的時候出現(xiàn)提示: 然后試試LOAD程序到板子里,居然可以正常運行,能夠但不運行。 不知道上面的紅色提示是怎么回事?…

查看全部問答∨

有負(fù)電勢的反相電平轉(zhuǎn)換電路

系統(tǒng)中不同的地方要不同的電壓,存儲器1.8V,MCU 5V,F(xiàn)PGA 3.3V等等。 這是一款簡單而快速的電平轉(zhuǎn)換電路,可以將輸入時鐘調(diào)節(jié)為適應(yīng)正、負(fù)電壓電平。 電平轉(zhuǎn)換電路包括快速切換的晶體管Q1和Q2。用戶選擇電平轉(zhuǎn)換為高和轉(zhuǎn)換為低,這是直流偏置 ...…

查看全部問答∨
小廣播
設(shè)計資源 培訓(xùn) 開發(fā)板 精華推薦

最新單片機文章

 
EEWorld訂閱號

 
EEWorld服務(wù)號

 
汽車開發(fā)圈

 
機器人開發(fā)圈

電子工程世界版權(quán)所有 京ICP證060456號 京ICP備10001474號-1 電信業(yè)務(wù)審批[2006]字第258號函 京公網(wǎng)安備 11010802033920號 Copyright ? 2005-2025 EEWORLD.com.cn, Inc. All rights reserved