當(dāng)前位置:首頁(yè) > 公眾號(hào)精選 > 嵌入式大雜燴
[導(dǎo)讀]【推薦】 本次分享的是好友Mculover666創(chuàng)建的一個(gè)開源項(xiàng)目解讀專欄,通過這個(gè)專欄我們可以了解、學(xué)習(xí)一些很實(shí)用的開源項(xiàng)目的用法及設(shè)計(jì)思想,強(qiáng)烈推薦!文末有交流群,我偶爾也在群里窺屏學(xué)習(xí),群內(nèi)大佬云集,技術(shù)交流氛圍很好~ 嵌入式開源項(xiàng)目精選專欄 本專


【推薦】 本次分享的是好友Mculover666創(chuàng)建的一個(gè)開源項(xiàng)目解讀專欄,通過這個(gè)專欄我們可以了解、學(xué)習(xí)一些很實(shí)用的開源項(xiàng)目的用法及設(shè)計(jì)思想,強(qiáng)烈推薦!文末有交流群,我偶爾也在群里窺屏學(xué)習(xí),群內(nèi)大佬云集,技術(shù)交流氛圍很好~


嵌入式開源項(xiàng)目精選專欄

本專欄由Mculover666創(chuàng)建,主要內(nèi)容為尋找嵌入式領(lǐng)域內(nèi)的優(yōu)質(zhì)開源項(xiàng)目,一是幫助開發(fā)者使用開源項(xiàng)目實(shí)現(xiàn)更多的功能,二是通過這些開源項(xiàng)目,學(xué)習(xí)大佬的代碼及背后的實(shí)現(xiàn)思想,提升自己的代碼水平,和其它專欄相比,本專欄的優(yōu)勢(shì)在于:

不會(huì)單純的介紹分享項(xiàng)目,還會(huì)包含作者親自實(shí)踐的過程分享,甚至還會(huì)有對(duì)它背后的設(shè)計(jì)思想解讀。

目前本專欄包含的開源項(xiàng)目有:

如果您自己編寫或者發(fā)現(xiàn)的開源項(xiàng)目不錯(cuò),歡迎留言或者私信投稿到本專欄,分享獲得雙倍的快樂!

1. SFUD

本期給大家?guī)淼拈_源項(xiàng)目是 SFUD,一款串行 Flash 通用驅(qū)動(dòng)庫(kù),作者armink,目前收獲 407 個(gè) star,遵循 MIT 開源許可協(xié)議。

SFUD全稱Serial Flash Universal Driver,是一款開源的串行 SPI Flash 通用驅(qū)動(dòng)庫(kù),由于現(xiàn)有市面的串行 Flash 種類居多,各個(gè) Flash 的規(guī)格及命令存在差異, SFUD 就是為了解決這些 Flash 的差異現(xiàn)狀而設(shè)計(jì)。

SFUD的特點(diǎn)在于:

  • 支持 SPI/QSPI 接口
  • 面向?qū)ο笤O(shè)計(jì)(同時(shí)支持多個(gè) Flash 對(duì)象)
  • 可靈活裁剪、擴(kuò)展性強(qiáng)
  • 支持 4 字節(jié)地址

項(xiàng)目地址:https://github.com/armink/SFUD

2. 移植SFUD

2.1. 移植思路

在移植過程中主要參考兩個(gè)資料:項(xiàng)目的readme文檔和demo工程。

對(duì)于這些開源項(xiàng)目,其實(shí)移植起來也就兩步:

  • ① 添加源碼到裸機(jī)工程中;
  • ② 實(shí)現(xiàn)需要的接口即可;

2.2. 準(zhǔn)備裸機(jī)工程

本文中我使用的是小熊派IoT開發(fā)套件,主控芯片為STM32L431RCT6:板載Flash型號(hào)為W25Q64JV,大小64Mbit,與STM32的QSPI接口相連:

移植之前需要準(zhǔn)備一份裸機(jī)工程,我使用STM32CubeMX生成,需要初始化以下配置:

  • 配置SPI Flash通信接口(SPI或QSPI)
  • 配置一個(gè)串口用于打印信息
  • printf重定向

具體過程請(qǐng)參考:

使用CubeMX配置好SPI或QSPI通信即可,不用編寫W25Q64驅(qū)動(dòng)。

2.3. 添加SFUD到工程中

① 復(fù)制源碼到工程中:② 在keil中添加 SFUD 組件的源碼文件:

  • src\sfud.c:SFUD核心功能源碼;
  • src\sfud_sfdp.c:讀取并分析SFDP功能源碼;
  • port\sfud_port.c:SFUD移植接口;

③ 將sfud/inc頭文件路徑添加到keil中:

2.4. 實(shí)現(xiàn)SFUD移植接口

SFUD的移植接口都已經(jīng)寫好了,在sfud_port.c文件中,只需要在函數(shù)體中添加代碼即可。

① 底層SPI/QSPI讀寫接口:

/**
 * SPI write data then read data
 */

static sfud_err spi_write_read(const sfud_spi *spi, const uint8_t *write_buf, size_t write_size, uint8_t *read_buf, size_t read_size);

② 如果使用的是QSPI通信方式,還需要實(shí)現(xiàn)快速讀取數(shù)據(jù)的接口:

/**
 * QSPI fast read data
 */

static sfud_err qspi_read(const struct __sfud_spi *spi, uint32_t addr, sfud_qspi_read_cmd_format *qspi_read_cmd_format, uint8_t *read_buf, size_t read_size);

③ SFUD底層使用的SPI/QSPI接口和SPI設(shè)備對(duì)象初始化接口:

sfud_err sfud_spi_port_init(sfud_flash *flash);

關(guān)于SFUD底層所抽象出來的SPI設(shè)備對(duì)象,在接下來的設(shè)計(jì)思想解讀章節(jié)中會(huì)詳細(xì)講述。

本文中所使用的裸機(jī)工程是基于HAL庫(kù)的,在SFUD源碼的Demo中也有一份HAL庫(kù)的工程,因?yàn)?strong style="color: black;">基于HAL庫(kù)的移植接口實(shí)現(xiàn)都是一樣的,所以我直接將Demo中的sfud_port.c文件復(fù)制過來替換:復(fù)制過來之后,如果使用的不是STM32L4系列的芯片,則需要修改sfud_port.c中包含的頭文件:

2.5. 配置SFUD

SFUD的核心功能配置文件在sfud_cfg.h,修改說明如下:修改完了之后,還需要去修改剛剛復(fù)制替換的sfud_port.c文件,與剛剛填寫的配置信息相對(duì)應(yīng):至此,SFUD移植、配置完成,接下來就可以愉快的使用了!

3. 使用SFUD

使用時(shí)包含頭文件:

#include <sfud.h>

3.1. 初始化SFUD

初始化SFUD的API如下,該函數(shù)會(huì)初始化 Flash 設(shè)備表中的全部設(shè)備:

sfud_err sfud_init(void);

在QSPI模式下,SFUD 對(duì)于 QSPI 模式的支持僅限于快速讀命令,通過該函數(shù)可以配置 Flash 所使用的 QSPI 總線的實(shí)際支持的數(shù)據(jù)線最大寬度,例如:1 線(默認(rèn)值,即傳統(tǒng)的 SPI 模式)、2 線、4 線:

sfud_err sfud_qspi_fast_read_enable(sfud_flash *flash, uint8_t data_line_width);

所以,在main函數(shù)中編寫如下初始化函數(shù):

/* USER CODE BEGIN 2 */

/* SFUD初始化 */
if(sfud_init() != SFUD_SUCCESS)
{
 printf("SFUD init fail.\r\n");
}
/* 使能QSPI快讀 */
sfud_qspi_fast_read_enable(sfud_get_device(SFUD_W25Q64_DEVICE_INDEX), 1);

/* USER CODE END 2 */

編譯、下載之后,可以在串口終端中看到SFUD打印的日志:SFUD初始化Flash設(shè)備成功后進(jìn)行接下來的讀寫測(cè)試。

3.2. Flash擦除/讀寫操作

① 讀取Flash數(shù)據(jù):

sfud_err sfud_read(const sfud_flash *flash, uint32_t addr, size_t size, uint8_t *data);

② 擦除 Flash 數(shù)據(jù):

sfud_err sfud_erase(const sfud_flash *flash, uint32_t addr, size_t size);

③ 往Flash寫數(shù)據(jù):

sfud_err sfud_write(const sfud_flash *flash, uint32_t addr, size_t size, const uint8_t *data);

接下來使用作者編寫的demo測(cè)試。

首先在main.c開頭編寫代碼,開辟一塊緩沖區(qū)用于存放測(cè)試數(shù)據(jù):

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */

/* SFUD讀寫Flash數(shù)據(jù)測(cè)試的緩沖區(qū) */
#define SFUD_DEMO_TEST_BUFFER_SIZE                     1024
static uint8_t sfud_demo_test_buf[SFUD_DEMO_TEST_BUFFER_SIZE];

/* SFUD讀寫Flash數(shù)據(jù)測(cè)試函數(shù) */
void sfud_demo(uint32_t addr, size_t size, uint8_t *data);

/* USER CODE END 0 */

然后在main.c最后添加測(cè)試函數(shù):

/* USER CODE BEGIN 4 */
/**
 * SFUD demo for the first flash device test.
 *
 * @param addr flash start address
 * @param size test flash size
 * @param size test flash data buffer
 */

void sfud_demo(uint32_t addr, size_t size, uint8_t *data)
{
    sfud_err result = SFUD_SUCCESS;
    extern sfud_flash *sfud_dev;
    const sfud_flash *flash = sfud_get_device(SFUD_W25Q64_DEVICE_INDEX);
    size_t i;
    /* prepare write data */
    for (i = 0; i < size; i++)
    {
        data[i] = i;
    }
    /* erase test */
    result = sfud_erase(flash, addr, size);
    if (result == SFUD_SUCCESS)
    {
        printf("Erase the %s flash data finish. Start from 0x%08X, size is %zu.\r\n", flash->name, addr, size);
    }
    else
    {
        printf("Erase the %s flash data failed.\r\n", flash->name);
        return;
    }
    /* write test */
    result = sfud_write(flash, addr, size, data);
    if (result == SFUD_SUCCESS)
    {
        printf("Write the %s flash data finish. Start from 0x%08X, size is %zu.\r\n", flash->name, addr, size);
    }
    else
    {
        printf("Write the %s flash data failed.\r\n", flash->name);
        return;
    }
    /* read test */
    result = sfud_read(flash, addr, size, data);
    if (result == SFUD_SUCCESS)
    {
        printf("Read the %s flash data success. Start from 0x%08X, size is %zu. The data is:\r\n", flash->name, addr, size);
        printf("Offset (h) 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F\r\n");
        for (i = 0; i < size; i++)
        {
            if (i % 16 == 0)
            {
                printf("[%08X] ", addr + i);
            }
            printf("%02X ", data[i]);
            if (((i + 1) % 16 == 0) || i == size - 1)
            {
                printf("\r\n");
            }
        }
        printf("\r\n");
    }
    else
    {
        printf("Read the %s flash data failed.\r\n", flash->name);
    }
    /* data check */
    for (i = 0; i < size; i++)
    {
    
        if (data[i] != i % 256)
        {
            printf("Read and check write data has an error. Write the %s flash data failed.\r\n", flash->name);
            break;
        }
    }
    if (i == size)
    {
        printf("The %s flash test is success.\r\n", flash->name);
    }
}

/* USER CODE END 4 */

main函數(shù)中,SFUD初始化代碼之后,調(diào)用該函數(shù)進(jìn)行Flash測(cè)試:

/* 測(cè)試Flash讀寫 */
sfud_demo(0sizeof(sfud_demo_test_buf), sfud_demo_test_buf);

編譯、下載,在串口終端中查看結(jié)果:

3.3. 移植前后內(nèi)存占用情況

SFUD中獲取Flash信息有兩種方式:

  • 使用SFDP 參數(shù)方式:開關(guān)宏 SFUD_USING_SFDP;
  • 使用庫(kù)自帶的 Flash 參數(shù)信息表:開關(guān)宏 SFUD_USING_FLASH_INFO_TABLE;

本文中兩種方式都開啟,所以移植之后較大,實(shí)際使用中可以視情況關(guān)閉這兩個(gè)功能。

SFDP功能關(guān)閉后,只會(huì)查詢?cè)搸?kù)在 /sfud/inc/sfud_flash_def.h 中提供的 Flash 信息表,代碼量會(huì)降低,但是軟件適配性也隨之降低。

查表功能關(guān)閉后,該庫(kù)只驅(qū)動(dòng)支持 SFDP 規(guī)范的 Flash,也會(huì)適當(dāng)?shù)慕档筒糠执a量。

一般情況下上述二者必須要選擇一個(gè),在實(shí)際使用時(shí)視情況而定,但是也可以兩者都不開啟,直接指定好具體的某款 Flash 參數(shù)。

4. SFUD設(shè)計(jì)思想解讀

4.1. Flash設(shè)備對(duì)象

SFUD中最重要的就是Flash設(shè)備對(duì)象,一切操作都是對(duì)這個(gè)Flash設(shè)備對(duì)象進(jìn)行的,每個(gè)Flash設(shè)備對(duì)象獨(dú)立,所以SFUD也支持系統(tǒng)中存在多個(gè)Flash設(shè)備對(duì)象。

Flash設(shè)備對(duì)象管理著Flash存儲(chǔ)器的所有信息,原型在sfud_def.h中,定義如下:

/**
 * serial flash device
 */

typedef struct {
    char *name;                                  /**< serial flash name */
    size_t index;                                /**< index of flash device information table  @see flash_table */
    sfud_flash_chip chip;                        /**< flash chip information */
    sfud_spi spi;                                /**< SPI device */
    bool init_ok;                                /**< initialize OK flag */
    bool addr_in_4_byte;                         /**< flash is in 4-Byte addressing */
    struct {
        void (*delay)(void);                     /**< every retry's delay */
        size_t times;                            /**< default times for error retry */
    } retry;
    void *user_data;                             /**< some user data */

#ifdef SFUD_USING_QSPI
    sfud_qspi_read_cmd_format read_cmd_format;   /**< fast read cmd format */
#endif

#ifdef SFUD_USING_SFDP
    sfud_sfdp sfdp;                              /**< serial flash discoverable parameters by JEDEC standard */
#endif

} sfud_flash, *sfud_flash_t;

其中Flash設(shè)備的通信接口信息由 sfud_spi 對(duì)象管理,包括SPI讀寫數(shù)據(jù)函數(shù),加鎖解鎖函數(shù)定義如下:

/**
 * SPI device
 */

typedef struct __sfud_spi {
    /* SPI device name */
    char *name;
    /* SPI bus write read data function */
    sfud_err (*wr)(const struct __sfud_spi *spi, const uint8_t *write_buf, size_t write_size, uint8_t *read_buf,
                   size_t read_size);
#ifdef SFUD_USING_QSPI
    /* QSPI fast read function */
    sfud_err (*qspi_read)(const struct __sfud_spi *spi, uint32_t addr, sfud_qspi_read_cmd_format *qspi_read_cmd_format,
                          uint8_t *read_buf, size_t read_size);
#endif
    /* lock SPI bus */
    void (*lock)(const struct __sfud_spi *spi);
    /* unlock SPI bus */
    void (*unlock)(const struct __sfud_spi *spi);
    /* some user data */
    void *user_data;
} sfud_spi, *sfud_spi_t;

4.2. JESD216 SFDP標(biāo)準(zhǔn)

SFDP全稱 Serial Flash Discoverable Parameter,它是 JEDEC (固態(tài)技術(shù)協(xié)會(huì))制定的串行 Flash 功能的參數(shù)表標(biāo)準(zhǔn)。

該標(biāo)準(zhǔn)規(guī)定了,每個(gè) Flash 中會(huì)存在一個(gè)參數(shù)表,該表中會(huì)存放 Flash 容量、寫粒度、擦除命令、地址模式等 Flash 規(guī)格參數(shù)。目前,除了部分廠家舊款 Flash 型號(hào)會(huì)不支持該標(biāo)準(zhǔn),其他絕大多數(shù)新出廠的 Flash 均已支持 SFDP 標(biāo)準(zhǔn)。

所以 SFUD 在初始化時(shí)會(huì)優(yōu)先讀取 SFDP 表參數(shù),以達(dá)到SFUD在支持SFDP標(biāo)準(zhǔn)的Flash上全部適用的效果,更加通用。

那么SFDP標(biāo)準(zhǔn)的內(nèi)容是什么呢?SFDP標(biāo)準(zhǔn)強(qiáng)制規(guī)范必須要有:

  • SFDP標(biāo)題頭
  • 1st參數(shù)頭
  • JEDEC Flash基本參數(shù)表格

SFDP標(biāo)題頭一般為“S”“F”“U”“D”,如果能讀取出這四個(gè)字符,則認(rèn)為該款Flash支持SFDP標(biāo)準(zhǔn),比如在sfud_sfdp源碼中校驗(yàn)代碼如下:

/* check SFDP header */
if (!(header[0] == 'S' &&
      header[1] == 'F' &&
      header[2] == 'D' &&
      header[3] == 'P')) {
    SFUD_DEBUG("Error: Check SFDP signature error. It's must be 50444653h('S' 'F' 'D' 'P').");
    return false;
}

接下來是一些預(yù)留空內(nèi)容,屬于廠商可選內(nèi)容,F(xiàn)lash廠商可以在這些空白內(nèi)容中添加自己的廠商ID識(shí)別號(hào)、SFDP版本號(hào)、參數(shù)長(zhǎng)度以及存放參數(shù)表格的地址指針,比如讀取W25Q64的結(jié)果中顯示:接下來的 JEDEC Flash基本參數(shù)表格里面規(guī)范和定義了該器件的一些最基本的讀取方式、指令內(nèi)容、扇區(qū)大小和芯片容量等信息:

4.3. 添加庫(kù)目前不支持的 Flash

如果你使用的Flash型號(hào)比較老或者不支持SFDP,SFUD庫(kù)當(dāng)然考慮到了這一點(diǎn),所以提供了Flash設(shè)備參數(shù)表,在sfdu_flash_def.h文件的 SFUD_FLASH_CHIP_TABLE 就能看到當(dāng)前所有支持的 Flash:如果你使用的Flash型號(hào)既不支持SFDP,也不在此Flash設(shè)備參數(shù)表中,那么就需要手動(dòng)添加到該設(shè)備參數(shù)表中才可以正常使用。

具體的添加方式請(qǐng)參考SFUD項(xiàng)目的README文檔中2.5節(jié),講述的非常詳細(xì)。

5. 項(xiàng)目工程源碼獲取和問題交流

目前我將SFUD源碼、我移植到小熊派STM32L431RCT6開發(fā)板的工程源碼上傳到了QQ群里(包含好幾份HAL庫(kù),QQ相對(duì)速度快點(diǎn)),可以在QQ群里下載,有問題也可以在群里交流,當(dāng)然也歡迎大家分享出來自己移植的工程到QQ群里:放上QQ群二維碼:

接收更多精彩文章及資源推送,歡迎訂閱我的微信公眾號(hào):『mculover666』。


免責(zé)聲明:本文內(nèi)容由21ic獲得授權(quán)后發(fā)布,版權(quán)歸原作者所有,本平臺(tái)僅提供信息存儲(chǔ)服務(wù)。文章僅代表作者個(gè)人觀點(diǎn),不代表本平臺(tái)立場(chǎng),如有問題,請(qǐng)聯(lián)系我們,謝謝!

本站聲明: 本文章由作者或相關(guān)機(jī)構(gòu)授權(quán)發(fā)布,目的在于傳遞更多信息,并不代表本站贊同其觀點(diǎn),本站亦不保證或承諾內(nèi)容真實(shí)性等。需要轉(zhuǎn)載請(qǐng)聯(lián)系該專欄作者,如若文章內(nèi)容侵犯您的權(quán)益,請(qǐng)及時(shí)聯(lián)系本站刪除。
換一批
延伸閱讀

9月2日消息,不造車的華為或?qū)⒋呱龈蟮莫?dú)角獸公司,隨著阿維塔和賽力斯的入局,華為引望愈發(fā)顯得引人矚目。

關(guān)鍵字: 阿維塔 塞力斯 華為

倫敦2024年8月29日 /美通社/ -- 英國(guó)汽車技術(shù)公司SODA.Auto推出其旗艦產(chǎn)品SODA V,這是全球首款涵蓋汽車工程師從創(chuàng)意到認(rèn)證的所有需求的工具,可用于創(chuàng)建軟件定義汽車。 SODA V工具的開發(fā)耗時(shí)1.5...

關(guān)鍵字: 汽車 人工智能 智能驅(qū)動(dòng) BSP

北京2024年8月28日 /美通社/ -- 越來越多用戶希望企業(yè)業(yè)務(wù)能7×24不間斷運(yùn)行,同時(shí)企業(yè)卻面臨越來越多業(yè)務(wù)中斷的風(fēng)險(xiǎn),如企業(yè)系統(tǒng)復(fù)雜性的增加,頻繁的功能更新和發(fā)布等。如何確保業(yè)務(wù)連續(xù)性,提升韌性,成...

關(guān)鍵字: 亞馬遜 解密 控制平面 BSP

8月30日消息,據(jù)媒體報(bào)道,騰訊和網(wǎng)易近期正在縮減他們對(duì)日本游戲市場(chǎng)的投資。

關(guān)鍵字: 騰訊 編碼器 CPU

8月28日消息,今天上午,2024中國(guó)國(guó)際大數(shù)據(jù)產(chǎn)業(yè)博覽會(huì)開幕式在貴陽(yáng)舉行,華為董事、質(zhì)量流程IT總裁陶景文發(fā)表了演講。

關(guān)鍵字: 華為 12nm EDA 半導(dǎo)體

8月28日消息,在2024中國(guó)國(guó)際大數(shù)據(jù)產(chǎn)業(yè)博覽會(huì)上,華為常務(wù)董事、華為云CEO張平安發(fā)表演講稱,數(shù)字世界的話語(yǔ)權(quán)最終是由生態(tài)的繁榮決定的。

關(guān)鍵字: 華為 12nm 手機(jī) 衛(wèi)星通信

要點(diǎn): 有效應(yīng)對(duì)環(huán)境變化,經(jīng)營(yíng)業(yè)績(jī)穩(wěn)中有升 落實(shí)提質(zhì)增效舉措,毛利潤(rùn)率延續(xù)升勢(shì) 戰(zhàn)略布局成效顯著,戰(zhàn)新業(yè)務(wù)引領(lǐng)增長(zhǎng) 以科技創(chuàng)新為引領(lǐng),提升企業(yè)核心競(jìng)爭(zhēng)力 堅(jiān)持高質(zhì)量發(fā)展策略,塑強(qiáng)核心競(jìng)爭(zhēng)優(yōu)勢(shì)...

關(guān)鍵字: 通信 BSP 電信運(yùn)營(yíng)商 數(shù)字經(jīng)濟(jì)

北京2024年8月27日 /美通社/ -- 8月21日,由中央廣播電視總臺(tái)與中國(guó)電影電視技術(shù)學(xué)會(huì)聯(lián)合牽頭組建的NVI技術(shù)創(chuàng)新聯(lián)盟在BIRTV2024超高清全產(chǎn)業(yè)鏈發(fā)展研討會(huì)上宣布正式成立。 活動(dòng)現(xiàn)場(chǎng) NVI技術(shù)創(chuàng)新聯(lián)...

關(guān)鍵字: VI 傳輸協(xié)議 音頻 BSP

北京2024年8月27日 /美通社/ -- 在8月23日舉辦的2024年長(zhǎng)三角生態(tài)綠色一體化發(fā)展示范區(qū)聯(lián)合招商會(huì)上,軟通動(dòng)力信息技術(shù)(集團(tuán))股份有限公司(以下簡(jiǎn)稱"軟通動(dòng)力")與長(zhǎng)三角投資(上海)有限...

關(guān)鍵字: BSP 信息技術(shù)
關(guān)閉
關(guān)閉