精品国产人成在线_亚洲高清无码在线观看_国产在线视频国产永久2021_国产AV综合第一页一个的一区免费影院黑人_最近中文字幕MV高清在线视频

0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

FreeRTOS優(yōu)化與錯(cuò)誤排查方法有哪些

汽車電子技術(shù) ? 來(lái)源:物聯(lián)網(wǎng)IoT開發(fā) ? 作者:杰杰 ? 2023-02-14 09:59 ? 次閱讀

寫在前面

主要是為剛接觸 FreeRTOS 的用戶指出那些新手通常容易遇到的問(wèn)題。這里把最主要的篇幅放在棧溢出以及棧溢出檢測(cè)上,因?yàn)闂O嚓P(guān)的問(wèn)題是初學(xué)者遇到最多的問(wèn)題。

printf-stdarg.c

當(dāng)調(diào)用 C 標(biāo)準(zhǔn)庫(kù) 的函數(shù)時(shí),棧空間使用量可能會(huì)急劇上升,特別是 IO 與字符串處理函數(shù),比如 sprintf()、printf()等。在 FreeRTOS 源碼包中有一個(gè)名為 printf-stdarg.c 的文件。這個(gè)文件實(shí)現(xiàn)了一個(gè)棧效率優(yōu)化版的小型 sprintf()、printf(),可以用來(lái)代替標(biāo)準(zhǔn) C 庫(kù)函數(shù)版本。在大多數(shù)情況下,這樣做可以使得調(diào)用 sprintf()及相關(guān)函數(shù)的任務(wù)對(duì)棧空間的需求量小很多。

可能很多人都不知道freertos中有這樣子的一個(gè)文件,它放在第三方資料中,路徑為“ FreeRTOSv9.0.0\\FreeRTOS-Plus\\Demo\\FreeRTOS_Plus_UDP_and_CLI_LPC1830_GCC ”,我們發(fā)布工程的時(shí)候就無(wú)需依賴 C 標(biāo)準(zhǔn)庫(kù) ,這樣子就能減少棧的使用,能優(yōu)化不少空間。

該文件源碼(部分):

1static int print( char **out, const char *format, va_list args )
 2{
 3    register int width, pad;
 4    register int pc = 0;
 5    char scr[2];
 6
 7    for (; *format != 0; ++format) {
 8        if (*format == '%') {
 9            ++format;
10            width = pad = 0;
11            if (*format == '\\0') break;
12            if (*format == '%') goto out;
13            if (*format == '-') {
14                ++format;
15                pad = PAD_RIGHT;
16            }
17            while (*format == '0') {
18                ++format;
19                pad |= PAD_ZERO;
20            }
21            for ( ; *format >= '0' && *format <= '9'; ++format) {
22                width *= 10;
23                width += *format - '0';
24            }
25            if( *format == 's' ) {
26                register char *s = (char *)va_arg( args, int );
27                pc += prints (out, s?s:"(null)", width, pad);
28                continue;
29            }
30            if( *format == 'd' || *format == 'i' ) {
31                pc += printi (out, va_arg( args, int ), 10, 1, width, pad, 'a');
32                continue;
33            }
34            if( *format == 'x' ) {
35                pc += printi (out, va_arg( args, int ), 16, 0, width, pad, 'a');
36                continue;
37            }
38            if( *format == 'X' ) {
39                pc += printi (out, va_arg( args, int ), 16, 0, width, pad, 'A');
40                continue;
41            }
42            if( *format == 'u' ) {
43                pc += printi (out, va_arg( args, int ), 10, 0, width, pad, 'a');
44                continue;
45            }
46            if( *format == 'c' ) {
47                /* char are converted to int then pushed on the stack */
48                scr[0] = (char)va_arg( args, int );
49                scr[1] = '\\0';
50                pc += prints (out, scr, width, pad);
51                continue;
52            }
53        }
54        else {
55        out:
56            printchar (out, *format);
57            ++pc;
58        }
59    }
60    if (out) **out = '\\0';
61    va_end( args );
62    return pc;
63}
64
65int printf(const char *format, ...)
66{
67    va_list args;
68
69    va_start( args, format );
70    return print( 0, format, args );
71}
72
73int sprintf(char *out, const char *format, ...)
74{
75    va_list args;
76
77    va_start( args, format );
78    return print( &out, format, args );
79}
80
81
82int snprintf( char *buf, unsigned int count, const char *format, ... )
83{
84    va_list args;
85
86    ( void ) count;
87
88    va_start( args, format );
89    return print( &buf, format, args );
90}

使用的例子與 C 標(biāo)準(zhǔn)庫(kù)基本一樣:

1int main(void)
 2{
 3    char *ptr = "Hello world!";
 4    char *np = 0;
 5    int i = 5;
 6    unsigned int bs = sizeof(int)*8;
 7    int mi;
 8    char buf[80];
 9
10    mi = (1 << (bs-1)) + 1;
11    printf("%s\\n", ptr);
12    printf("printf test\\n");
13    printf("%s is null pointer\\n", np);
14    printf("%d = 5\\n", i);
15    printf("%d = - max int\\n", mi);
16    printf("char %c = 'a'\\n", 'a');
17    printf("hex %x = ff\\n", 0xff);
18    printf("hex %02x = 00\\n", 0);
19    printf("signed %d = unsigned %u = hex %x\\n", -3, -3, -3);
20    printf("%d %s(s)%", 0, "message");
21    printf("\\n");
22    printf("%d %s(s) with %%\\n", 0, "message");
23    sprintf(buf, "justif: \\"%-10s\"\\n", "left"); printf("%s", buf);
24    sprintf(buf, "justif: \"%10s\"\\n", "right"); printf("%s", buf);
25    sprintf(buf, " 3: %04d zero padded\\n", 3); printf("%s", buf);
26    sprintf(buf, " 3: %-4d left justif.\\n", 3); printf("%s", buf);
27    sprintf(buf, " 3: %4d right justif.\\n", 3); printf("%s", buf);
28    sprintf(buf, "-3: %04d zero padded\\n", -3); printf("%s", buf);
29    sprintf(buf, "-3: %-4d left justif.\\n", -3); printf("%s", buf);
30    sprintf(buf, "-3: %4d right justif.\\n", -3); printf("%s", buf);
31
32    return 0;
33}

棧計(jì)算

每個(gè)任務(wù)都獨(dú)立維護(hù)自己的棧空間, 任務(wù)棧空間總量在任務(wù)創(chuàng)建時(shí)進(jìn)行設(shè)定。uxTaskGetStackHighWaterMark()主要用來(lái)查詢指定任務(wù)的運(yùn)行歷史中, 其棧空間還差多少就要溢出。這個(gè)值被稱為棧空間的 High Water Mark

函數(shù)原型:

1UBaseType_t uxTaskGetStackHighWaterMark( TaskHandle_t xTask )

想要使用它,需要將對(duì)應(yīng)的宏定義打開:INCLUDE_uxTaskGetStackHighWaterMark

函數(shù)描述:

參數(shù) 說(shuō)明
xTask 被查詢?nèi)蝿?wù)的句柄如果傳入 NULL 句柄,則任務(wù)查詢的是自身?xiàng)?臻g的高水線
返回值 任務(wù)棧空間的實(shí)際使用量會(huì)隨著任務(wù)執(zhí)行和中斷處理過(guò)程上下浮動(dòng)。uxTaskGetStackHighWaterMark()返回從任務(wù)啟動(dòng)執(zhí)行開始的運(yùn)行歷史中,棧空間具有的最小剩余量。這個(gè)值即是棧空間使用達(dá)到最深時(shí)的剩下的未使用的棧空間。這個(gè)值越是接近 0,則這個(gè)任務(wù)就越是離棧溢出不遠(yuǎn)。

如果不知道怎么計(jì)算任務(wù)棧大小,就使用這個(gè)函數(shù)進(jìn)行統(tǒng)計(jì)一下,然后將任務(wù)運(yùn)行時(shí)最大的棧空間作為任務(wù)棧空間的80%大小即可。即假設(shè)統(tǒng)計(jì)得到的任務(wù)棧大小為常量 A ,那么在創(chuàng)建線程的時(shí)候需要 X 大小的空間,那么 X * 80% = A ,算到的 X 作為任務(wù)棧大小就差不多了。

運(yùn)行時(shí)棧檢測(cè)

FreeRTOS 包含兩種運(yùn)行時(shí)棧檢測(cè)機(jī)制,由 FreeRTOSConfig.h 中的配置常量configCHECK_FOR_STACK_OVERFLOW 進(jìn)行控制。這兩種方式都會(huì)增加上下切換開銷。

棧溢出鉤子函數(shù)(或稱回調(diào)函數(shù))由內(nèi)核在檢測(cè)到棧溢出時(shí)調(diào)用。要使用棧溢出鉤子函數(shù),需要進(jìn)行以下配置:

  • 在 FreeRTOSConfig.h 中把 configCHECK_FOR_STACK_OVERFLOW 設(shè)為 1 或者 2
  • 提供鉤子函數(shù)的具體實(shí)現(xiàn),采用下面所示的函數(shù)名和函數(shù)原型。
1void vApplicationStackOverflowHook( xTaskHandle *pxTask, signed portCHAR *pcTaskName );

補(bǔ)充說(shuō)明:

  • 棧溢出鉤子函數(shù)只是為了使跟蹤調(diào)試棧空間錯(cuò)誤更容易,而無(wú)法在棧溢出時(shí)對(duì)其進(jìn)行恢復(fù)。函數(shù)的入口參數(shù)傳入了任務(wù)句柄和任務(wù)名,但任務(wù)名很可能在溢出時(shí)已經(jīng)遭到破壞。
  • 棧溢出鉤子函數(shù)還可以在中斷的上下文中進(jìn)行調(diào)用
  • 某些微控制器在檢測(cè)到內(nèi)存訪問(wèn)錯(cuò)誤時(shí)會(huì)產(chǎn)生錯(cuò)誤異常,很可能在內(nèi)核調(diào)用棧溢出鉤子函數(shù)之前就觸發(fā)了錯(cuò)誤異常中斷。

方法1

當(dāng) configCHECK_FOR_STACK_OVERFLOW 設(shè)置為 1 時(shí)選用方法 1

任務(wù)被交換出去的時(shí)候,該任務(wù)的整個(gè)上下文被保存到它自己的棧空間中。這時(shí)任務(wù)棧的使用應(yīng)當(dāng)達(dá)到了一個(gè)峰值。當(dāng) configCHECK_FOR_STACK_OVERFLOW 設(shè)為1 時(shí),內(nèi)核會(huì)在任務(wù)上下文保存后檢查棧指針是否還指向有效棧空間。一旦檢測(cè)到棧指針的指向已經(jīng)超出任務(wù)棧的有效范圍,棧溢出鉤子函數(shù)就會(huì)被調(diào)用。

方法 1 具有較快的執(zhí)行速度,但棧溢出有可能發(fā)生在兩次上下文保存之間,這種情況不會(huì)被檢測(cè)到,因?yàn)檫@種檢測(cè)方式僅在任務(wù)切換中檢測(cè)。

方法2

configCHECK_FOR_STACK_OVERFLOW 設(shè)為 2 就可以選用方法 2 。方法 2在方法 1 的基礎(chǔ)上進(jìn)行了一些補(bǔ)充。

當(dāng)創(chuàng)建任務(wù)時(shí),任務(wù)棧空間中就預(yù)置了一個(gè)標(biāo)記。方法 2 會(huì)檢查任務(wù)棧的最后 20個(gè)字節(jié)的數(shù)據(jù),查看預(yù)置在這里的標(biāo)記數(shù)據(jù)是否被覆蓋。如果最后 20 個(gè)字節(jié)的標(biāo)記數(shù)據(jù)與預(yù)設(shè)值不同,則棧溢出鉤子函數(shù)就會(huì)被調(diào)用。

方法 2 沒(méi)有方法 1 的執(zhí)行速度快,但測(cè)試僅僅 20 個(gè)字節(jié)相對(duì)來(lái)說(shuō)也是很快的。這種方法應(yīng)該可以檢測(cè)到任何時(shí)候發(fā)生的棧溢出,雖然理論上還是有可能漏掉一些情況,但這些情況幾乎是不可能發(fā)生的。

其它常見(jiàn)錯(cuò)誤

在一個(gè) Demo 應(yīng)用程序中增加了一個(gè)簡(jiǎn)單的任務(wù),導(dǎo)致應(yīng)用程序崩潰

可能的情況:

  1. 任務(wù)創(chuàng)建時(shí)需要在內(nèi)存堆中分配空間。許多 Demo 應(yīng)用程序定義的堆空間大小只夠用于創(chuàng)建 Demo 任務(wù)——所以當(dāng)任務(wù)創(chuàng)建完成后,就沒(méi)有足夠的剩余空間來(lái)增加其它的 任務(wù),隊(duì)列或信號(hào)

  2. 空閑任務(wù)是在 vTaskStartScheduler()調(diào)用中自動(dòng)創(chuàng)建的。如果由于內(nèi)存不足而無(wú)法創(chuàng)建空閑任務(wù),vTaskStartScheduler()會(huì)直接返回。所以一般在調(diào)用 vTaskStartScheduler()后加上一條空循環(huán)for(;;) / while(1)可以使這種錯(cuò)誤更加容易調(diào)試。

    如果要添加更多的任務(wù),可以增加內(nèi)存堆空間大小(修改配置文件),或是刪掉一些已存在的 Demo任務(wù)。

在中斷中調(diào)用一個(gè) API 函數(shù),導(dǎo)致應(yīng)用程序崩潰

需要做的第一件事是檢查中斷是否導(dǎo)致了棧溢出。

然后檢查API接口是否正確 ,除了具有后綴為FromISR函數(shù)名的 API 函數(shù),千萬(wàn)不要在中斷服務(wù)程序中調(diào)用其它 API 函數(shù)。

除此之外,還需要注意中斷的優(yōu)先級(jí):

FreeRTOSConfig.h文件中可以配置系統(tǒng)可管理的最高中斷優(yōu)先級(jí)數(shù)值,宏定義configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY是用于配置basepri寄存器的,當(dāng)basepri設(shè)置為某個(gè)值的時(shí)候,會(huì)讓系統(tǒng)不響應(yīng)比該優(yōu)先級(jí)低的中斷,而優(yōu)先級(jí)比之更高的中斷則不受影響。就是說(shuō)當(dāng)這個(gè)宏定義配置為5的時(shí)候,中斷優(yōu)先級(jí)數(shù)值在0、1、2、3、4的這些中斷是不受FreeRTOS管理的,不可被屏蔽, 同時(shí)也不能調(diào)用FreeRTOS中的API函數(shù)接口 ,而中斷優(yōu)先級(jí)在5到15的這些中斷是受到系統(tǒng)管理,可以被屏蔽的,也可以調(diào)用FreeRTOS中的API函數(shù)接口。

臨界區(qū)無(wú)法正確嵌套

除了 taskENTER_CRITICA()和 taskEXIT_CRITICAL(),千萬(wàn)不要在其它地方修改控制器的中斷使能位或優(yōu)先級(jí)標(biāo)志。這兩個(gè)宏維護(hù)了一個(gè)嵌套深度計(jì)數(shù),所以只有當(dāng)所有的嵌套調(diào)用都退出后計(jì)數(shù)值才會(huì)為 0,也才會(huì)使能中斷。

在調(diào)度器啟動(dòng)前應(yīng)用程序就崩潰了

這個(gè)問(wèn)題我也會(huì)遇到,如果一個(gè)中斷會(huì)產(chǎn)生上下文切換,則這個(gè)中斷不能在調(diào)度器啟動(dòng)之前使能。這同樣適用于那些需要讀寫隊(duì)列或信號(hào)量的中斷。在調(diào)度器啟動(dòng)之前,不能進(jìn)行上下文切換。

還有一些 API 函數(shù)不能在調(diào)度器啟動(dòng)之前調(diào)用。在調(diào)用 vTaskStartScheduler()之前,最好是限定只使用創(chuàng)建任務(wù),隊(duì)列和信號(hào)量的 API 函數(shù)。

比如有一些初始化需要中斷的,或者在初始化完成的時(shí)候回產(chǎn)生一個(gè)中斷,這些驅(qū)動(dòng)的初始化最好放在一個(gè)任務(wù)中進(jìn)行,我是這樣子處理的,在main函數(shù)中創(chuàng)建一個(gè)任務(wù),在任務(wù)中進(jìn)行bsp初始化,然后再創(chuàng)建消息隊(duì)列、信號(hào)量、互斥量、事件以及任務(wù)等操作。

在調(diào)度器掛起時(shí)調(diào)用 API 函數(shù),導(dǎo)致應(yīng)用程序崩潰

調(diào)用 vTaskSuspendAll()使得調(diào)度器掛起,而喚醒調(diào)度器調(diào)用 xTaskResumeAll()。千萬(wàn)不要在調(diào)度器掛起時(shí)調(diào)用其它 API 函數(shù)。

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問(wèn)題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • 函數(shù)
    +關(guān)注

    關(guān)注

    3

    文章

    4304

    瀏覽量

    62429
  • FreeRTOS
    +關(guān)注

    關(guān)注

    12

    文章

    483

    瀏覽量

    62002
  • 棧空間
    +關(guān)注

    關(guān)注

    0

    文章

    5

    瀏覽量

    5435
收藏 人收藏

    評(píng)論

    相關(guān)推薦

    使用匯編知識(shí)排查疑難問(wèn)題的方法

    那么,本篇文章,我將再介紹一個(gè)使用匯編知識(shí)排查疑難問(wèn)題的方法,希望對(duì)大家有所幫助。
    發(fā)表于 07-27 10:31 ?644次閱讀

    利用符號(hào)模擬技術(shù)優(yōu)化錯(cuò)誤診斷方法

    來(lái)優(yōu)化基于區(qū)域模型錯(cuò)誤診斷過(guò)程的方法。該方法首先使用基于區(qū)域模型錯(cuò)誤診斷方法中電路劃分
    發(fā)表于 07-05 08:05

    電子書:STM32-FREERTOS快速學(xué)習(xí)知識(shí)解密

    ——應(yīng)用場(chǎng)景3STM32單片機(jī)中,FreeRTOS RAM使用情況及優(yōu)化方法4FreeRTOS中列表和列表項(xiàng)插入函數(shù)分析5FreeRTOS+
    發(fā)表于 05-09 14:30

    STM32 FreeRTOS RAM 使用情況及優(yōu)化方法實(shí)用資料分享~

    的一般方法,并給出在 FreeRTOS優(yōu)化 RAM 使用的方法,也由衷的期望讀者在使用其他 RTOS 時(shí),可以通過(guò)相似的思路來(lái)解決問(wèn)題。Free
    發(fā)表于 01-26 14:10

    什么方法可以查看FreeRTOS任務(wù)的運(yùn)行狀態(tài)呢

    什么方法可以查看FreeRTOS任務(wù)的運(yùn)行狀態(tài)呢?怎樣去查看FreeRTOS任務(wù)的運(yùn)行狀態(tài)呢?
    發(fā)表于 11-02 07:59

    CH579無(wú)規(guī)律進(jìn)入hardfault錯(cuò)誤如何排查

    CH579 程序運(yùn)行時(shí),偶爾進(jìn)入 hardfault 錯(cuò)誤 ,沒(méi)有什么規(guī)律,如何排查?謝謝
    發(fā)表于 07-26 07:24

    GPIO無(wú)法觸發(fā)中斷常規(guī)排查方法哪些?

    1、電源域是否打開 2、IOMUX是否設(shè)置對(duì) 3、是否配置了中斷方式,外部電平是否滿足條件 4、是否為輸入狀態(tài) 備注:這次分享的是,我們做展銳平臺(tái)GPIO排查方法,不同平臺(tái)、不同版本、不同項(xiàng)目都會(huì)
    發(fā)表于 11-24 16:11

    建立一個(gè)方法和套路來(lái)對(duì) Load 高問(wèn)題排查

    講解 Linux Load 高如何排查的話題屬于老生常談了,但多數(shù)文章只是聚焦了幾個(gè)點(diǎn),缺少整體排查思路的介紹。所謂 “授人以魚不如授人以漁"。本文試圖建立一個(gè)方法和套路,來(lái)幫助讀者對(duì) Load 高問(wèn)題
    的頭像 發(fā)表于 12-28 14:18 ?5414次閱讀
    建立一個(gè)<b class='flag-5'>方法</b>和套路來(lái)對(duì) Load 高問(wèn)題<b class='flag-5'>排查</b>

    FreeRTOS中的API函數(shù)功能分析及調(diào)用方法

    FreeRTOS中的API函數(shù)功能分析及調(diào)用方法說(shuō)明。
    發(fā)表于 03-26 11:50 ?32次下載

    單片機(jī)硬錯(cuò)誤排查方法

    HardFault 錯(cuò)誤調(diào)試定位方法1、首先更改 startup.s 的啟動(dòng)文件,把里面的 HardFault_Handler 代碼段換成下面的代碼:HardFault_Handler
    發(fā)表于 12-16 16:54 ?0次下載
    單片機(jī)硬<b class='flag-5'>錯(cuò)誤</b><b class='flag-5'>排查</b><b class='flag-5'>方法</b>

    科普系列:CAN總線錯(cuò)誤幀及排查方法簡(jiǎn)介

    作者|蒹葭小編|吃不飽CAN幀多種格式,錯(cuò)誤幀作為CAN幀中獨(dú)特的一種,了解其作用,類型與產(chǎn)生原因,對(duì)于進(jìn)行測(cè)試以及開發(fā)有很大的幫助,本文將對(duì)錯(cuò)誤幀的相關(guān)基礎(chǔ)知識(shí)以及后續(xù)的分析排查進(jìn)
    的頭像 發(fā)表于 02-23 15:11 ?3057次閱讀
    科普系列:CAN總線<b class='flag-5'>錯(cuò)誤</b>幀及<b class='flag-5'>排查</b><b class='flag-5'>方法</b>簡(jiǎn)介

    雅馬哈YS/YSM系列貼片機(jī)故障排查方法

    雅馬哈YS/YSM系列貼片機(jī)故障排查方法
    的頭像 發(fā)表于 09-13 10:05 ?3404次閱讀
    雅馬哈YS/YSM系列貼片機(jī)故障<b class='flag-5'>排查</b><b class='flag-5'>方法</b>

    常見(jiàn)的電源適配器故障及排查方法哪些?

    常見(jiàn)的電源適配器故障及排查方法哪些? 電源適配器故障是使用電子設(shè)備時(shí)經(jīng)常遇到的問(wèn)題之一。合理排查和解決電源適配器故障是確保電子設(shè)備正常運(yùn)行的重要步驟。本文將詳細(xì)介紹常見(jiàn)的電源適配器故
    的頭像 發(fā)表于 11-24 14:08 ?6858次閱讀

    腳本錯(cuò)誤scripterror怎么解決

    分析和排查。以下是一些常見(jiàn)的解決腳本錯(cuò)誤方法: 檢查語(yǔ)法錯(cuò)誤: 仔細(xì)檢查腳本中的代碼,看是否拼寫錯(cuò)誤
    的頭像 發(fā)表于 11-26 14:46 ?8039次閱讀

    如何用示波器排查CAN的各種錯(cuò)誤幀呢?

    如何用示波器排查CAN的各種錯(cuò)誤幀呢? 導(dǎo)言: 控制器局域網(wǎng)絡(luò)(Controller Area Network,CAN)是一種常用的現(xiàn)場(chǎng)總線通信協(xié)議,廣泛應(yīng)用于汽車電子系統(tǒng)、工業(yè)自動(dòng)化等領(lǐng)域。然而
    的頭像 發(fā)表于 12-07 11:09 ?1113次閱讀