部分終端產(chǎn)品在出廠前需要在MCU Flash特定的地址處寫入特定的數(shù)據(jù),該數(shù)據(jù)可能是產(chǎn)品序列號、版本號或特定的配置參數(shù)等信息。比如我們希望在Flash 0x08030000地址處寫入4個字節(jié)的數(shù)據(jù)0x11、0x22、0x33、0x44,如何實現(xiàn)該功能呢?
方法一、直接寫Flash
按照MCU寫Flash的方式,程序中先擦除地址對應的頁,然后按字寫入對應的內(nèi)容即可。此方法需要增加一段操作flash的代碼,不太方便,不推薦使用。
方法二、直接改寫hex文件或者bin文件
hex文件需要熟悉其文件格式,bin文件也要計算文件大小,理論上可以實現(xiàn),但是也比較麻煩,不推薦使用。
方法三、離線燒錄器配置
如果使用離線燒錄器的話,比如創(chuàng)芯工坊,可以在上位機的序列號配置欄中進行配置,注意序列號增量要改為0。

這種方式相比前兩種方式要簡單很多,但是它只能配置一個參數(shù),如果有多個參數(shù)需要寫入那就無法實現(xiàn)了。
方法四、分散加載文件實現(xiàn)
通過修改分散加載文件的方法,可以實現(xiàn)該功能。以常用的KEIL MDK為例。
假設默認的sct文件如下:
; *************************************************************
; *** Scatter-Loading Description File generated by uVision ***
; *************************************************************
LR_IROM1 0x08000000 0x00040000 { ; load region size_region
ER_IROM1 0x08000000 0x00040000 { ; load address = execution address
*.o (RESET, +First)
*(InRoot$$Sections)
.ANY (+RO)
.ANY (+XO)
}
RW_IRAM1 0x20000000 0x00007000 { ; RW data
.ANY (+RW +ZI)
}
}
修改如下:
LR_IROM1 0x08000000 0x00030000 { ; load region size_region
ER_IROM1 0x08000000 0x00030000 { ; load address = execution address
*.o (RESET, +First)
*(InRoot$$Sections)
.ANY (+RO)
.ANY (+XO)
}
RW_IRAM1 0x20000000 0x00007000 { ; RW data
.ANY (+RW +ZI)
}
}
LR_IROM2 0x08030000 0x00010000 { ; load region size_region
ER_IROM2 0x08030000 0x00010000 { ; user data
*(myflash)
}
}
main.c文件中增加如下語句即可:const uint8_t my_flash_table[] __attribute__((used,section("myflash"))) = { 0x11,0x22,0x33,0x44 };
這樣hex文件里就寫入了相應的值:

原始hex文件如下:

注意一定要寫上used,用 used 防止被鏈接器剔除。如果這么寫:const uint8_t my_flash_table[] __attribute__((section("myflash"))) = { 0x11,0x22,0x33,0x44 };
編譯就會報如下warning,實際是沒有生效。

方法五、直接絕對定位實現(xiàn)
這是最直接的方法,如果用的是Arm Compiler 5 (AC5),可以直接使用
attribute((at(address)))或者 attribute((section(".ARM.__at_address")))。如果你的項目已經(jīng)遷移到Arm Compiler 6 (AC6),則必須使用 attribute((section(".ARM.__at_address"))),因為AC6不再支持原始的 at屬性。
根據(jù)使用的是AC5或AC6,main.c文件中增加如下語句即可:
const uint32_t gflashdata __attribute__((at(0x08030000))) = 0x44332211;
或者
const uint32_t gflashdata __attribute__((section(".ARM.__AT_0x8030000"))) = 0x44332211;hex文件變?yōu)槿缦拢?/span>

和方法四不同的是0x08030000之前其他未使用的Flash地址會被填充為0x00,這是因為鏈接器在生成輸出鏡像時的行為,它會把映像從最小地址擴展到最高已定義地址;中間沒有明確內(nèi)容的“空洞”會被填充為 0x00。

可為什么地址0x08030004-0x08030007也被修改了呢?這四個字節(jié)的數(shù)據(jù)是0x00、0x24、0xF4、0x00。 從map文件可以看到,是因為鏈接器在該地址放置了 .data 的初始內(nèi)容。
對比不加入const uint32_t gflashdata attribute((at(0x08030000))) = 0x44332211; 原始的map文件:
引入這句話改變了Load base。在Keil MDK的map文件中,RW_IRAM1的 Load base(加載基地址)指的是存儲在Flash中的初始數(shù)據(jù)鏡像的起始地址。這些數(shù)據(jù)(主要是已初始化的全局變量)在程序上電后,需要被復制到RAM中的執(zhí)行地址(Exec Addr)才能被正確使用。
其實0x00、0x24、0xF4、0x00 對應的代碼中SystemCoreClock這個全局變量的值。
以上介紹的內(nèi)容,希望對大家有所幫助。
關注我們:
掃碼加入嵌入式交流群:
