|
轉(zhuǎn)自 | 瑞薩嵌入式小百科
在C語言編程過程中,由于計(jì)算需要,會(huì)使用各種各樣的變量,用于給需要訪問的地址取個(gè)名稱,方便編程中使用,代碼維護(hù)者也容易理解。
這里先給大家分享一個(gè)案例,讓大家意識(shí)到變量賦初始值的重要性。
案例:
某用戶在基于瑞薩的MCU:RA6T2做開發(fā)時(shí),發(fā)現(xiàn)一個(gè)問題,MCU發(fā)出的CAN數(shù)據(jù)幀總是莫名其妙的出錯(cuò)。
比如應(yīng)用中明明只使用了CAN的擴(kuò)展幀,但是使用捕捉工具總是能捕捉到遠(yuǎn)程幀,出現(xiàn)遠(yuǎn)程幀的情況毫無規(guī)律可言,有時(shí)添加一個(gè)定時(shí)器中斷,該現(xiàn)象就不會(huì)出現(xiàn)了,有時(shí)修改了代碼里某處跟CAN沒有任何關(guān)系的代碼,該問題又會(huì)出現(xiàn),過了兩周時(shí)間調(diào)試無果。
在介入Debug時(shí)發(fā)現(xiàn),他使用的是CAN擴(kuò)展幀,擴(kuò)展幀使用29位ID標(biāo)識(shí)符,而且對ID區(qū)數(shù)據(jù)定義了一個(gè)如下結(jié)構(gòu)體:
z0i3c45gg0f64025259010.png (1.75 KB, 下載次數(shù): 0)
下載附件
保存到相冊
z0i3c45gg0f64025259010.png
2024-11-26 07:33 上傳
他在需要發(fā)送CAN幀時(shí),申請一個(gè)如上結(jié)構(gòu)體的臨時(shí)變量can_id,在把can_id.id賦值后,再把該變量的地址傳遞給CAN的發(fā)送函數(shù),在發(fā)送函數(shù)里使用如下語句把id的數(shù)據(jù)寫入CAN的發(fā)送消息緩沖寄存器:
23ryrxoxhne64025259110.png (2.92 KB, 下載次數(shù): 0)
下載附件
保存到相冊
23ryrxoxhne64025259110.png
2024-11-26 07:33 上傳
如下圖,其中第30位的0表示數(shù)據(jù)幀,并不是遠(yuǎn)程幀,31位的1表示擴(kuò)展幀。
u5v0z3tnp3s64025259210.png (59.67 KB, 下載次數(shù): 2)
下載附件
保存到相冊
u5v0z3tnp3s64025259210.png
2024-11-26 07:33 上傳
用戶是把can_id的所有數(shù)據(jù)賦值給了CFDTMID0寄存器,假如can_id.dummy中第二個(gè)位是1,會(huì)有什么后果呢?CFDTMID0.TMRTR=1,即CAN會(huì)發(fā)送遠(yuǎn)程幀。
用戶又問:我沒有給dummy賦值啊,為什么dummy的第二個(gè)位會(huì)變成1呢?這就是問題所在了,就是因?yàn)樗麤]有給can_id.dummy賦值,所以can_id.dummy有可能為任意的值。下面詳細(xì)分析一下,為什么這個(gè)局部變量的值會(huì)隨意變化!
大家知道,變量根據(jù)存儲(chǔ)類型和用途,一般可以分成:全局變量和局部變量。全局變量,就是指分配了固定地址的變量,全局變量可以在整個(gè)代碼范圍內(nèi)使用。我們在申請全局變量時(shí),有時(shí)對它賦一個(gè)初始值,也時(shí)也不會(huì)賦初始值,在代碼上可能看不出有什么區(qū)別,但是編譯器在編譯程序時(shí),是區(qū)別對待他們的。
對于有初始化的變量,編譯器還需要在Code Flash里(代碼存儲(chǔ)區(qū))分配一段空間,把變量的初始值全部存儲(chǔ)在該區(qū)域里,并且在MCU的啟動(dòng)代碼里插入一段程序,把這些Code Flash區(qū)的初始值拷貝到變量對應(yīng)的RAM地址中。假如上面的can_id是全局變量,并且申明變量的同時(shí)并按下圖賦初始值:
3jtpdm4xhec64025259310.jpg (32.22 KB, 下載次數(shù): 1)
下載附件
保存到相冊
3jtpdm4xhec64025259310.jpg
2024-11-26 07:33 上傳
這時(shí)can_id.dummy=0,如果代碼中用戶沒有再賦值,它的值也不會(huì)變化,這樣就不會(huì)發(fā)生用戶的那個(gè)遠(yuǎn)程幀的問題了。對于沒有賦初始值的全局變量,編譯器只是分配RAM的地址,并不會(huì)修改RAM地址里的數(shù)據(jù),那么這個(gè)變量的值就會(huì)依賴于MCU啟動(dòng)時(shí)RAM里的值了。為了避免未賦值的全局變量出現(xiàn)上述的問題,我們一般會(huì)在MCU啟動(dòng)代碼里插入未賦初始值全局變量的清零操作,相當(dāng)于做了一個(gè)未賦初始值的全局變量的初始化賦值操作。
像上面的案例,can_id申請的是局部變量,這又是什么情況呢?
因?yàn)镸CU的RAM資源有限,為了最大限度的利用RAM,MCU會(huì)提前分配一塊RAM區(qū)域,叫堆棧區(qū),這塊區(qū)域大家共用,對于只需要在某個(gè)函數(shù)內(nèi)使用的變量,引入了局部變量概念。在開始執(zhí)行該函數(shù)時(shí),才從堆棧里分配地址給局部變量使用,函數(shù)執(zhí)行結(jié)束后,該變量占用的RAM區(qū)域被堆;厥,當(dāng)下次再調(diào)用該函數(shù),再重新分配RAM。因此對于局部變量,每次申請到的地址是不同的,該地址很可能是其它函數(shù)使用過并改寫數(shù)據(jù)了的,因此每次函數(shù)調(diào)用時(shí)can_id.dummy的數(shù)據(jù)是不確定的。因?yàn)槎褩^(qū)里的數(shù)據(jù)是被反復(fù)利用的,即使MCU的初始化代碼對堆棧區(qū)域做清零處理,也是沒有意義的。
由此看來,局部變量在申請的時(shí)候賦一個(gè)初始值,是非常有必要的。雖然有時(shí)候賦初始值沒有用,但是出現(xiàn)問題時(shí)常常是致命的,而且也是非常難以定位的,你可能覺得我的代碼里后面肯定會(huì)賦值的,但是后面維護(hù)該項(xiàng)目的其他工程師并不一定意識(shí)到這一點(diǎn)。像類似上面的案例,我在其他用戶當(dāng)中也是經(jīng)常見到的。因此軟件工程師在編程的時(shí)候,一定要養(yǎng)成局部變量賦初始值的習(xí)慣。
------------ END ------------
335xr01nqrd64025259410.jpg (176.09 KB, 下載次數(shù): 0)
下載附件
保存到相冊
335xr01nqrd64025259410.jpg
2024-11-26 07:33 上傳
MCU代碼自檢和診斷的常規(guī)方法
y3iq0oulfuh64025259510.jpg (161.73 KB, 下載次數(shù): 0)
下載附件
保存到相冊
y3iq0oulfuh64025259510.jpg
2024-11-26 07:33 上傳
一年一度MCU/MPU工業(yè)技術(shù)研討會(huì)亮點(diǎn)來了!
3krcnupr5ar64025259610.jpg (301.16 KB, 下載次數(shù): 1)
下載附件
保存到相冊
3krcnupr5ar64025259610.jpg
2024-11-26 07:33 上傳
瑞薩MCU這幾年的崛起之路 |
|