QCC300x — 外部Flash的讀寫操作

XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

XX  作       者:ZHS(文化人)

XX  聯繫方式:文章末尾Chat快問(或進羣:471144274

XX  版權聲明:原創文章,歡迎評論和轉載~轉載時能告訴我一聲就最好了

XX  要說的話:作者水平有限,難免有不足之處,懇請指正!

XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

1.    QCC300X 外部Flash的讀寫
        QCC300x是使用外部Flash片子,使用外部flash的好處就是成本下來了,大家都知道,CSR的片子一直是很貴的,這樣的片子就只能對成本要求不高的項目優先,在使用外部Flash時,我們一般默認使用32M(因爲官方配置就是這個參數,會讓使用方便的多),然而我們在使用過程往往使用不了這麼大的空間,現在我們就利用起來,一起來學習對外部Flash的應用。

        1.1.    爲外部flash分區
            要對外部Flash使用,必先是對外部Flash進行分區,上一節做Upgrade時有對分區文件說明,即XXX.ptn文件。
          需要注意的是外部的Flash可以作爲兩種用途使用,一種是隻讀的文件系統,另一種是未處理的連續數據分區(Raw Serial),只讀的文件系統是對於代碼,語音文件,用戶文件等保存於Flash中,但是這個需要以文件的形式打包寫入,在代碼中不可以被修改,僅僅可以讀取。
        未處理的連續數據分區,就是純粹的Flash數據,在程序中可以寫入和讀取。

        使用文件系統時我們需要掛載分區,使用Raw Serial分區時 不需要掛載分區。

        我們想實現一個在Flash中的數據寫入和讀取,所以Flash在原來的分區下添加一個Raw Serial分區:

        0, 8K, PS, (none) # For PS Store
        1, 32K, RO, i1107e_patch_bundle.xuv # Logical #0 : For DSP & firmware patches #0,1
        2, 32K, RO, (erase) # Logical #0 : For DSP & firmware patches #0,2
        3, 612K, RO, (erase) # Logical #1 Audio prompts #1,1
        4, 612K, RO, (erase) # Logical #1 Audio prompts #1,2
        5, 300K, RO, i1107e.xuv # Logical #2 Main application image and other files. #2,1
        6, 300K, RO, (erase) # Logical #2 Main application image and other files. #2,2
        7, 8K, RO, system_i1107e.xuv # Logical #3 PSFS  #3,1
        8, 8K, RO, (erase) # Logical #3 PSFS #3,2
        9, 8K, RS, (erase) 
        10, 8K, RS, (erase) 
        11, *, RS, (erase)
        即添加分區9和10,大小爲8K,分區類型是RS(Raw Serial),(erase)每次下載重新擦除。

        需要說明一下,這個分區文件涉及下載軟件Xide.exe的下載流程,新添加的分區儘量填寫每次下載擦除,否則會影響下載,
        我之前有調試過使用(none)會導致下載有很久的卡頓,導致下載時間超長。

        1.2.    Flash寫入數據
        Flash在分好區後就可以讀寫了,Flash的讀寫也是一個流的概論,寫入需要獲取一個Sink,讀取需要獲取一個Source ,需要注意的是Flash在沒有數據時是讀取不到數據的,直接讀取未寫入數據的Flash會是空。

        1.2.1.    獲取Flash的Sink
        獲取Sink的方法,ADK提供了兩個函數,可以獲取Flash的Sink,分別是
        Sink StreamPartitionOverwriteSink(partition_filesystem_devices device,
        uint16 partition) 
        Sink StreamPartitionResumeSink(partition_filesystem_devices device,
        uint16 partition,uint16 first_word); 

        可以在Partition 和Stream的代碼文件中找到。

        第一個函數重新寫入Flash,獲取的Sink寫入後會覆蓋之前寫入的數據。

        第二個函數是再次寫入Flash。第三個參數是需要寫入的起始地址,如果Sink中已經存在數據,可以使用
        uint32 PartitionSinkPosition(Sink sink)函數來獲取已經存在的Flash數據大小

        partition_filesystem_devices :是選擇分區文件的類型,我們需要選擇Flash
        Partition:這個參數就是我們分區時分的分區號,就是XXX.ptn文件中添加的9或10分區號。

        1.2.2.    設置Flash的寫入配置
        Flash在寫入是需要設置其寫入配置,配置需要如下函數設置:
        bool PartitionSetMessageDigest(Sink sink, partition_message_digest_type md_ty, uint16 *data, uint16 len) 
        這個函數可以設置Flash的配置,
        sink: The sink that is writing to the partition
        md_type: The type of message digest:
            PARTITION_MESSAGE_DIGEST_APP_SIGNATURE: Signed with the application DFU key (see note)
            PARTITION_MESSAGE_DIGEST_CRC: Filesystem CRC
            PARTITION_MESSAGE_DIGEST_SKIP: Do not perform verification
        data: pointer to the message digest
        len: length of message digest. 2 for CRC verification, 66 for signature verification 

        1.2.3.    獲取Flash的狀態
        這一步不是必要的,我們在操作Flash,如果需要查看看狀態可使用:
        bool PartitionGetInfo(partition_filesystem_devices device, uint16 partition,partition_info_key key, uint32 *value) 

        參數說明:
        device: The device to query. Set to PARTITION_SERIAL_FLASH to query the serial flash device.
        partition: The number of the partition to query.
        key: The type of information requested:
            PARTITION_INFO_IS_MOUNTED: Whether a partition is mounted or not (1 = mounted, 0 = unmounted).
            PARTITION_INFO_SIZE: The size of the partition in words.
            PARTITION_INFO_TYPE: The type of the partition (0 = unused, 1 = filesystem, 2 = PS Store).
        value: The pointer to return the query result to 

        1.2.4.    寫入Flash數據
        寫入數據是最後的一步,但是沒有前面的鋪墊,是不能寫入成功的,我們得到的分區Sink,就是我們寫入數據入口。

        拿到Sink後,我們不能盲目寫入,可以使用SinkSlack(Sink sink),查看一下Sink可以寫入的最大數據量,我測試過程中獲取到的是48,說明Flash每次最大隻能寫入48個字符,如果我們需要大量寫入,可以分批寫入數據。

        數據寫入:
        第一步:使用SinkMap(Sink sink) 獲取一個指針;SinkClaim(Sink sink ,uint16 extra) 聲明一下寫入的大小,爲了檢查能否寫入,如果返回0xFFFF,說明不能寫入。

        第二步:使用memcpy() 把需要寫入的數據搬運到SinkMap()指針指向的地址上,

        第三步:使用SinkFlush(Sink sink uint6 amount)實現Flash的真正寫入。

        第四步:使用SinkClose(Sink sink)關閉Sink
        需要注意的是:
        1,Sink寫入數據後,如果需要讀取,必先使用第四步關閉Sink才能讀取,但是使用SinkClose()關閉Sink後,不能再次對Flash寫入,需要使用重啓寫入函數對Flash重啓寫入。
        2,如果需要多次分批寫入Flash,不要SinkClose()關閉Sink,完全寫入完成後,再關閉Sink,關閉後,再次寫入Flash失效,需要重新覆蓋寫入,

        3,Sink再次寫入時,需要重新獲取Sink,獲取的Sink偏移需要PartitionSinkGetPosition函數獲取即可。

        如果上面的步驟沒有出錯,恭喜你,你的Flash數據寫入成功。

        1.2.5.    Flash寫入程序附錄
        示例代碼只是初步演示:

        Flash的寫入:        

        static  void  test_flash_resave_date(uint8 *data,uint16 len )
        {
                Sink sink;
                bool ok=FALSE;
                /* 獲取分區9 的Sink , flashsize:記錄Flash 寫入的大小*/
                sink = StreamPartitionResumeSink(PARTITION_SERIAL_FLASH, 9,flashsize);  
                if (!sink)
                {
                    printf("UPG: Failed to open raw partition %u for resume\n", 9);  
                    return;
                }
                /* 寫入Flash 配置*/
                 /* Disable the crc check */
                ok =  PartitionSetMessageDigest(sink, PARTITION_MESSAGE_DIGEST_SKIP,NULL, 0);

                printf(" Set Digest ok=%d , get pasition size %ld ,slack=size %d\n",ok,
                            PartitionSinkGetPosition(sink),
                            SinkSlack(sink));
                
               if((SinkClaim(sink, sizeof(data)) != 0xFFFF))
                {
                    memcpy(SinkMap(sink), data, len);
                    ok = SinkFlush(sink, len);
                    printf("SinkFlush ok=%d  \n",ok);
                }
                flashsize = PartitionSinkGetPosition(sink);           
        }

        如果需要寫入後結束,直接在函數後面添加SinkClose,如下:

        static  void  test_flash_resave_date(uint8 *data,uint16 len )
        {
            Sink sink;
            bool ok=FALSE;
            /* 獲取分區9 的Sink , flashsize:記錄Flash 寫入的大小*/
            sink = StreamPartitionResumeSink(PARTITION_SERIAL_FLASH, 9,flashsize);  
            if (!sink)
            {
                printf("UPG: Failed to open raw partition %u for resume\n", 9);  
                return;
            }
            /* 寫入Flash 配置*/
             /* Disable the crc check */
            ok =  PartitionSetMessageDigest(sink, PARTITION_MESSAGE_DIGEST_SKIP,NULL, 0);

            printf(" Set Digest ok=%d , get pasition size %ld ,slack=size %d\n",ok,
                        PartitionSinkGetPosition(sink),
                        SinkSlack(sink));
            
           if((SinkClaim(sink, sizeof(data)) != 0xFFFF))
            {
                memcpy(SinkMap(sink), data, len);
                ok = SinkFlush(sink, len);
                printf("SinkFlush ok=%d  \n",ok);
            }
            flashsize = PartitionSinkGetPosition(sink);     

            SinkClose(sink);      
        }
        注意1、獲取分區後,前面會有兩個字節的數據,不清楚是什麼,只能從第三個字節開始寫;
        注意2、SinkSlack(Sink sink)查看最大寫入數據量;
        注意3、SinkClaim(Sink sink ,uint16 extra) 聲明寫入的大小,返回值表示能否寫入,如果返回0xFFFF,說明不能寫入;
        注意4、封裝flash操作接口,可參考Upgrade_fw_if.c裏的接口;
        FlashDataIFPartitionOpen    
        FlashDataIFPartitionClose
        FlashDataIFPartitionWrite
        FlashDataIFPartitionRead
        FlashDataIFPartitionGetOffset
        FlashDataIFGetPhysPartitionNum
        FlashDataIFGetPhysPartitionSize
    
    typedef Sink FlashDataIFPartitionHdl;
    Sink FlashDataIFPartitionOpen(uint16 physPartition, uint16 Offset)
    {
        Sink sink;
        sink = StreamPartitionResumeSink(PARTITION_SERIAL_FLASH, physPartition, Offset);
        if(!sink)
        {
            printf("Failed to open raw partition %d for resum\r\n", physPartition);
            return 0;
        }
        PartitionSetMessageDigest(sink, PARTITION_MESSAGE_DIGEST_SKIP, NULL, 0);
        
        return (FlashDataIFPartitionHdl)(int)sink;
    }
    uint16 FlashDataIFPartitionClose(FlashDataIFPartitionHdl handle)
    {
        Sink sink = (Sink)(int)handle;
        if(!sink)
            return 1;
        if(!SinkClose(sink))
        {
            printf("Unable to close SINK\r\n");
            return 1;
        }
        return 0;
    }
    uint16 FlashDataIFPartitionWrite(FlashDataIFPartitionHdl handle, uint8 *data, uint16 len)
    {
        Sink sink = (Sink)(int)handle;
        
        if(!sink)
            return 0;
        if(SinkClaim(sink, len) == 0xFFFF)
        {
            printf("Failed to claim\r\n");
            return 0;
        }
        memcpy(SinkMap(sink), data, len);
        if(!SinkFlush(sink, len))
        {
            printf("Failed to flush data\r\n");
            return 0;
        }
        return len;
    }

        1.3.    Flash 讀取數據
        讀取數據和寫數據是一個反流程,需要獲取一個Source ,通過Source獲取Flash的數據。
        需要注意的是Flash的讀取是一次性讀取,獲取的Source包含了整個分區的數據,但是Source的大小僅僅最大是2K的數據,
        我們在使用過程,如果需要讀取較多的數據,可以分批讀取。

        1.3.1.    獲取Flash 的 Source
        獲取Source直接使用PartitionGetRawSerialSource函數獲取即可:
        Source PartitionGetRawSerialSource(partition_filesystem_devices device, uint16 partition); 
        參數說明:
        device: The device to query. Set to PARTITION_SERIAL_FLASH to query the serial flash device.
        partition: The number of the partition to query.

        1.3.2.    讀取Flash數據
        讀取數據時需要注意的是Flash必先是寫入數據了,否則獲取的Source就是一個空的數據,不可以使用。

        一切準備好的話,讀取Flash就比較簡單了,
        第一步:通過SourceMap() 獲取Flash的數據指針,
        第二步:通過SourceSize() 獲取數據的大小,
        需要注意的是:
        如果獲取的數據大於2K,可能實際數據會大於2k,可以使用SourceDrop()去掉已經讀取完的數據,在重複第一、二步驟分別讀取數據,直到數據讀取完成。

        第三步:通過SuorceClose() 關閉Source

        1.3.3.     讀取Flash程序附錄
        示例代碼只是初步演示:        

        static void test_flash_read_date(void)
        {   
            const uint8 *date;
            uint16 date_size,i;
            Source source_flash;
         
            source_flash  = PartitionGetRawSerialSource(PARTITION_SERIAL_FLASH,9);
            date = SourceMap(source_flash);
            date_size =  SourceSize(source_flash);

            printf("source  %X  addr Map  %p\n",(uint16 )source_flash, date);
            printf("flash Size = %d,",date_size);
           
            /*每次讀取500,直到讀取完成*/
            while(date_size>500)
            {
                printf("flash Size = %d,",date_size);
                for(i=0;i<500;i++)
                    printf("%x ",date[i]);
                printf("\n");
                SourceDrop(source_flash,500);
                date = SourceMap(source_flash);
                date_size =  SourceSize(source_flash);  
            }
                  
            if(date_size>0)
            {
                for(i=0;i<date_size;i++)
                printf("%x ",date[i]);
            }
            printf("\n");
            SourceClose(source_flash);
        }

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章