解決printf時導(dǎo)致死機的問題

我第一次使用CH32V307芯片,以下內(nèi)容內(nèi)容難免瑕疵,所以僅供參考,希望幫助有類似困惑的開發(fā)者。


系統(tǒng)啟動后在運行第一行printf時導(dǎo)致死機并進入HardFault,原因是heap內(nèi)存不足,C庫分配printf緩存失敗并導(dǎo)致后續(xù)故障。


我在sbrk函數(shù)中添加了一段日志輸出:

__attribute__((used))?void?*_sbrk(ptrdiff_t?incr)
{
????extern?char?_end[];
????extern?char?_heap_end[];
????static?char?*curbrk?=?_end;

????if?((curbrk?+?incr?<?_end)?||?(curbrk?+?incr?>?_heap_end)){
????????const?char?msg[]?=?"sbrk?overflow\r\n";
????????_write(0,?(char*)msg,?sizeof(msg));
????????return?NULL?-?1;
????}

????curbrk?+=?incr;
????return?curbrk?-?incr;
}

通過這段消息我才定位到是堆內(nèi)存溢出導(dǎo)致的故障。


進一步分析堆內(nèi)存為什么會溢出,通過檢查_end指針和_heap_end指針,發(fā)現(xiàn)有效的堆空間已經(jīng)不足1000字節(jié)。進一步分析鏈接腳本,發(fā)現(xiàn)官方提供的鏈接腳本沒有保證heap空間的最小值,當(dāng)用戶占用的RAM要接近最大值時,heap空間的長度沒有保證。

????.bss?:
????{
????????.?=?ALIGN(4);
????????PROVIDE(?_sbss?=?.);
????????*(.sbss*)
????????*(.gnu.linkonce.sb.*)
????????*(.bss*)
????????*(.gnu.linkonce.b.*)????????
????????*(COMMON*)
????????.?=?ALIGN(4);
????????PROVIDE(?_ebss?=?.);
????}?>RAM?AT>FLASH

????PROVIDE(?_end?=?_ebss);?????/*?heap的起始地址夾在bss段和stack段之間?*/
????PROVIDE(?end?=?.?);?????????/*?這中間的剩余空間大小不好控制?*/

????.stack?ORIGIN(RAM)?+?LENGTH(RAM)?-?__stack_size?:
????{
????????PROVIDE(?_heap_end?=?.?);????
????????.?=?ALIGN(4);
????????PROVIDE(_susrstack?=?.?);
????????.?=?.?+?__stack_size;
????????PROVIDE(?_eusrstack?=?.);
????????__freertos_irq_stack_top?=?.;
????}?>RAM


調(diào)整一下鏈接腳本:

ENTRY(?_start?)

__stack_size?=?2048;
__heap_size??=?2048;????????/*?顯式聲明heap空間大小?*/

SECTIONS
{
????/*?other?section?*/

????.bss?:
????{
????????.?=?ALIGN(4);
????????PROVIDE(?_sbss?=?.);
????????*(.sbss*)
????????*(.gnu.linkonce.sb.*)
????????*(.bss*)
????????*(.gnu.linkonce.b.*)
????????*(COMMON*)
????????.?=?ALIGN(4);
????????PROVIDE(?_ebss?=?.);???/*?這句可以調(diào)整到下面?*/
????}?>RAM

????.heap?:
????{
????????.?=?ALIGN(4);
????????PROVIDE?(?end?=?.?);
????????PROVIDE?(?_end?=?.?);
????????.?=?.?+?__heap_size;
????????.?=?ALIGN(4);
????????PROVIDE(?_heap_end?=?.?);
????}?>RAM

????.stack?ORIGIN(RAM)?+?LENGTH(RAM)?-?__stack_size?:
????{
????????.?=?ALIGN(4);
????????PROVIDE(_susrstack?=?.?);
????????.?=?.?+?__stack_size;
????????PROVIDE(?_eusrstack?=?.);
????????__freertos_irq_stack_top?=?.;
????}?>RAM
}


以上這個鏈接腳本將固定heap空間為__heap_size設(shè)置的大小,heap段和stack段之間剩余的空間將可被stack使用,可避免棧溢出。如果想將剩余空間分配到heap,可將PROVIDE( _heap_end = . );移動到stack段的起始位置。


在我的開發(fā)環(huán)境中,默認情況下使用printf會使用1500字節(jié)左右的heap空間,如果你希望printf減小heap的使用,可將標(biāo)準(zhǔn)輸出的行緩沖模式改為0緩沖模式,在第一次使用printf函數(shù)前執(zhí)行setvbuf函數(shù)。

int?main(void)
{
????setvbuf(stdout,?NULL,?0,?_IONBF);???????/*?零緩沖模式?*/

????NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
????SystemCoreClockUpdate();
????Delay_Init();
????USART_Printf_Init(115200);

????printf("SystemClk:%d\r\n",SystemCoreClock);
????printf("ChipID:%08x\r\n",?DBGMCU_GetCHIPID()?);

????while(1);
}


這樣可大幅減少heap空間的使用。


當(dāng)出現(xiàn)故障時我們可以在HardFault函數(shù)中輸出必要的信息來協(xié)助分析問題:

#include?

void?HardFault_Handler(void)?__attribute__((interrupt("WCH-Interrupt-fast")));

extern?int?_write(int?fd,?char?*buf,?int?size);
static?int?fault_printf(const?char?*format,?...)
{
????#define?FAULT_MSG_LEN?(32)
????static?char?buf[FAULT_MSG_LEN];
????int?len;
????va_list?args;
????va_start(args,?format);
????len?=?vsnprintf(buf,?FAULT_MSG_LEN,?format,?args);
????va_end(args);
????if(len?>?FAULT_MSG_LEN)
????????len?=?FAULT_MSG_LEN;
????_write(0,?buf,?len);
????return?len;
}

void?HardFault_Handler(void)
{
????fault_printf("HardFault:\r\n");
????fault_printf("MSTATUS:?%#x\r\n",?__get_MSTATUS());
????fault_printf("MCAUSE?:?%#x\r\n",?__get_MCAUSE());
????fault_printf("MEPC???:?%#x\r\n",?__get_MEPC());

????if((__get_MCAUSE()?&?0x80000000)?==?0){
????????uint16_t?exc_id?=?__get_MCAUSE()?&?7;
????????const?char*?cause[8]?=?{
????????????"Code?unaligned",
????????????"Code?can't?access",
????????????"Code?error",
????????????"Break?point",
????????????"Load?MEM?unaligned",
????????????"Load?MEM?error",
????????????"Store?MEM?unaligned",
????????????"Store?MEM?error",
????????};
????????if(exc_id?<?8){
????????????fault_printf("%s\r\n",?cause[exc_id]);
????????}
????}
????while?(1)
????{
????}
}


在HardFault使用了一個自定義的print,這是為了避免使用C庫的printf時出現(xiàn)內(nèi)存分配失敗后導(dǎo)致的一系列連續(xù)故障。在HardFault中出現(xiàn)新的HardFault故障會把事情搞得更加復(fù)雜。


最后,我們來調(diào)整一下MounRiver軟件的設(shè)置,這樣可以讓我們能夠清楚直觀的看到Flash和RAM分別用了多少。


在工程設(shè)置中定位到C/C++build - Setting中,在鏈接選項中添加一個額外的鏈接選項 -Wl,--print-memory-usage


1710471903383150.jpg

這樣比直接顯示各個字段的大小要更加直觀。

1710471903200868.jpg

上圖顯示RAM用了93%,當(dāng)RAM占用接近最大時,需要及時調(diào)整代碼中各個模塊的內(nèi)存分配。

您好,可以按照下圖勾選使用我們的打印庫試一下,應(yīng)該不會有堆內(nèi)存溢出的問題。使用我們打印庫時,main函數(shù)中setvbuf那個函數(shù)就不需要加了。后續(xù)若有問題,可郵箱(lzs@wch.cn)和我溝通。

image.png


只有登錄才能回復(fù),可以選擇微信賬號登錄

国产91精品新入口,国产成人综合网在线播放,九热这里只有精品,本道在线观看,美女视频a美女视频,韩国美女激情视频,日本美女pvp视频