-
我還是那個觀點,一定要站在發明者的角度來看問題,只有這樣,一切問題才都能迎刃而解。因為模電的內容就是發明---使用---發現問題---改進---再發明—再使用的過程,是我們學習前人發明和使用的東西。
我們就以二極管和三極管為例,二極管是控制導線中電子的流動方向,而三極管是控制導線中流動電子的多少。這也是“電子技術”的根本。理論搞明白了實驗就簡單了。
下面主要是以三極管為例來說明導線中電流的控制。
我們都非常熟悉家用手電筒電路,手電筒電路中有“三要素”,即電源VCC、燈泡L(或者說負載Rc)、 開關K,如下圖所示。
現在,我們不想用手動方式去實現開關K的合上以及斷開,我們想用一個信號去控制一個器件來實現電路的“通和斷”。 要想控制一根導線中的電流,首先要把這根導線斷開,斷開的兩端我們分別叫做C端和E端(C和E實際上是輸出回路的兩端)。
如果我們在C和E之間加個器件,這個器件如果能使電流從C端流進并能從E端流出來(因為C和E本來就是我斷開的一個回路的兩端),同時這個電流又能被我們加的信號所控制住,那么這個器件就成功了。(一定要注意,我們要實現什么目標,我們要控制一個電回路的通和斷) 為了實現上述要求,接下來我們就在C-E之間放一個NPN(或PNP)結構的半導體,可是,現在的問題是,在這種情況下無論怎樣在C和E之間加電源 (不擊穿情況下) ,C-E這根導線始終都不會有電流(其實這種情況下,C-E之間是有穿透電流的(它是由少子引起的),因其非常小,這里忽略不計,這也是半導體材料存在的缺點。實際上,我們不希望它存在)。
我們又知道,電子流動的方向與人們定義電流的方向相反(這是因為當時人們以為電線里流過的是電流),所以,我們將中間半導體引出一個電極(B極)。
在B-E之間(實際上是加在發射結上,見PN結特性)加一個正向電壓,這時發射區就會向基區發射電子從而形成E極流出的電流Ie,但是,要想實現這個電流是從C端入、從E端出,則必須要把發射區發射的這些電子都收集到C極去,這樣我們需要在C和E之間加正向電壓,使集電結處于反向擊穿狀態,使電子能順利收集到C極,這個收集電子的能力要比發射電子的能力強,它就像一個大口袋,你發射區發射多少我就收多少(這樣就能理解三極管輸出特性曲線了,當B極電流一定時,隨著CE電壓的增加,C極電流就不再增加了,因為B極電流一定時,發射區發射的電子數量就一定了,你收集的能力再強也要不到多余的電子了),這樣,這個器件就成了,可以實現電流從C端到E端(因為當初我假設它們之間是被我斷開的導線兩端),最理想的是流進C端的電流就等于E端流出的電流,同時這個電流又被一個BE電壓(或信號)控制,但是,三極管不是一個理想的器件,因為C端電流不等于E端電流,有一部分電流流過B極,我們盡量使C端電流等于E端電流,所以,這就是為什么在工藝上要使基區濃度要低而且還要薄,同時集電結的面積還要大的根本原因。 談一談Ic受Ib控制的問題: 通過前面的敘述,我們已經知道發射極電流Ie受發射結電壓控制,由于我們采取了工藝上的措施,使得集電極電流Ic近似等于發射極電流Ie,這樣就可以說集電極電流Ic受發射結電壓控制。我們又從三極管輸入特性曲線可知,當Vbe和Ib的關系處于特性曲線的近似直線的位置時,基極電流Ib與發射結電壓就成線性關系,這樣,可以說集電極電流Ic與基極電流Ib就成比例關系。往往我們會站在不同角度來看問題,我們從電流放大的角度來看時,剛才說過集電極電流Ic比基極電流Ib大很多,同時它們又成比例關系,因此,在進行計算的時候就說成是集電極電流Ic受基極電流Ib控制。這其實是人們站的角度不同而已(從電流放大的角度來看的),其實,集電極電流Ic還是由發射結電壓控制的,等到了高頻小信號模型的時候,就會說集電極電流受發射結電壓控制了。 Uce電壓的作用是收集電子的,它的大小不能決定Ic的大小,從三極管輸出特性曲線可以看到,當Ib一定時(也就是Ube一定時),即使Uce增加,Ic就不變了,但是,由于半導體中有少子存在,使得輸出特性曲線隨著Uce增加而有些上翹,其實這是半導體材料存在的問題。實際上,Ie是受從輸入端看進去的發射結電壓控制的(可以參見三極管高頻小信號模型),加Uce電壓的時候發射結已經處于導通了,它的影響不在發射結而在集電結,加Uce電壓是為了讓Ic基本等于Ie,所以說Ic受發射結電壓控制,人們為了計算方便把這種控制折算成受Ib控制,就是因為說成這樣,使得人們不太容易理解三極管工作的原理。從輸出回路受輸入回路信號控制的角度來看,Ic不是由Ie控制的,但是,Ic其實是由Ie帶來的,所以,也可以說Ic受Ie影響的,這也得受三極管制造工藝影響,如果拿兩個背靠背二極管的話,怎么也不行。 盡管三極管不是一個理想器件,但是,它的發明已經是具有劃時代意義了。由于它的B極還有少量電流,因為這個電流的存在意味著輸入回路有耗能,如果我不耗能就能控制住你輸出回路的電流,那這個便宜就大了,所以,后來人們發明了場效應管。其實,發明場效應管的思想也是與三極管一樣的,就是為了用一個電壓來控制導線中的電流,只是這回輸入回路幾乎不耗能了,同時,器件兩端的電流相等了。
從使用者的角度(非設計者)來看看三極管的應用: 三極管的兩個基本應用分別是“可控開關”和“信號的線性放大”。 可控開關:C和E之間相當于一個可控開關(當然。這個開關有一定的參數要求),當B-E之間沒有加電壓時,C-E之間截止(C-E之間斷開);而當B-E之間電壓加的很大,發射區發射的電子數量就多,C極和E極的電流就很大,如果輸出回路中有負載時(注意,輸出回路沒有負載CE之間就不會飽和),由于輸出回路的電源電壓絕大部分都加到負載上了,CE之間的電壓就會很小,CE之間就處于飽和狀態,CE之間相當于短路。在飽和情況下,盡管C極電流比基極電流大,但是,C極電流與輸入回路的電流(基極電流)不成β的比例關系。 以最簡單的電路為例,我們家里都有手電筒,手電筒有三個要素(具有普遍意義):電源、燈泡(負載)和開關,這里的開關需要直接手動進行合上與斷開,用三極管代替這個開關我們就能實現用信號來控制,計算機在遠端就能控制這個回路。控制高壓、大電流的還請大家看看IGBT等功率芯片及模塊,那是真震撼。 從另一方面看飽和:從輸出特性曲線可以看到,IB一定時VCE電壓不用很大,那個輸出特性曲線就彎曲變平了,這說明收集電子的電壓VCE不用很大就行,其實不到1V就行,但是,實際上我們在輸出回路都是加一個電壓很大的電源,你再加大VCE也沒有用,我們看到,IB一定時VCE增加后對IC的大小沒有影響(理想情況),所以要想把發射的電子收集過去,VCE根本不用很大電壓。 但是,通常情況下,我們會在輸出回路加入一個負載,當負載兩端電壓小于電源電壓時,電源電壓的其它部分就加在CE兩端,此時三極管處于線性放大狀態。但是,負載兩端電壓的理論值大于電源電壓時,則三極管就處于飽和狀態,這種情況IC不用很大也行。 所以不要以為VCE一定很大三極管集電極才能收集到電子,可以看到收集電子的電壓很小就行。從電壓角度來看,集電極電流不一定很大,在選擇合適負載電阻的情況下,三極管也可以處于飽和狀態,所以,飽和與負載有關,如果電源電壓很大,那飽和時VCE就這么一點點電壓而言那當然是微不足道的,所以,很多地方就將它約等于零了,但是并不能說它沒有電子收集能力。 信號的線性放大:這種情況下,C極電流與B極電流成線性比例關系IC=βIB(BE之間電壓要大于死區電壓,同時,VCE不趨于零),而且,C極電流比B極電流大很多,前面已經知道,C極電流的大小受BE電壓控制(人們為了分析問題方便,將這種控制關系說成是C極電流受B極電流控制)。實際上,馬路上到處跑的汽車就是一個放大器,它是把駕駛員操作信號給放大了,它也是線性放大,是能量的放大,而多余的能量來自于燃燒的汽油。 模電這門課從三極管小信號模型開始的絕大多數內容都是講小信號放大問題,共射極、共集電極、共基極的4個電路是基本,其它的是由他們組合而成的,它們的電路組成、電路交直流分析、電路性能分析是關鍵。 其它的就是功率放大的問題、模擬集成運算放大器內部結構設計問題、運放的應用、如何減少非線性失真和放大穩定問題(負反饋)、正弦波產生(正反饋)等等。 模電從細節和總體上把握。 模電的學習: 從使用者的角度來看,其實,模電這門課并不難,學生往往被書中提到的所謂少子、多子、飄移、擴散等次要問題所迷惑,沒有抓住主要問題,有些問題是半導體材料本身存在缺陷導致的,人們為了克服這些缺陷而想出了各種解決辦法,所以,模電中有許多是人們想出的技巧和主意。從三極管三個電極連接的都是金屬的角度來看,金屬中只有自由電子的定向流動才有電流,金屬中哪有什么空穴之類的東西,如果把人們的視線停留在三極管的內部,那一定使人們不容易理解,如果你跳出來看問題,你就會理解科學家當時為什么要發明它,也會使你豁然開朗。但是,從設計者角度來看,需要考慮的問題就很多了,否則,你設計出來的器件性能就沒有人家設計的好,當然也就沒有市場了。如果誰能找到一種材料,而這種材料的性能比半導體特性還好,那么他一定會被全世界所敬仰。所以,學習模電的時候,一定要用工程思維來考慮問題,比如,為什么要發明它?它有什么用途?它可以解決什么問題?它有哪些不足?人們是如何改進的?等等。 再談可控開關: 三極管要工作在飽和或截止狀態,此時C和E之間相當于可控開關,B極加輸入信號,為了防止三極管損壞,B極要接限流電阻,余下的問題就是,所控制的負載應接在C極還是E極?它的功率有多大?驅動電壓多大?電流多大?你選的三極管能否勝任?不勝任怎么辦?改用什么器件?低壓和高壓如何隔離?等等。 再談信號的線性放大: 這種情況下,C極電流是B極電流的β倍,以三極管放大電路為例: (1)直流工作點問題,為什么要有直流工作點?什么原因引起工作點不穩定?采取什么措施穩定直流工作點? 為什么要有直流工作點?是因為PN結只有外加0.5V以上電壓時才有電流通過(硅材料),而我們要放大的微弱的交變信號幅度很小,將這個微弱的變化信號直接加到三極管的基極和射極之間,基極是沒有電流的,當然,集電極也不可能有電流。所以,我們在基極首先要加上直流工作電流后,三極管三個電極就都有直流電流了, 以NPN管子為例,共射、共基、共集電極三個電路的直流都是一個方向,無論三極管電路的哪種接法,它們的直流電流方向都是一樣的,在這基礎上,再在輸入端(發射結)加入微弱交流小信號后,這個微弱信號就會使基極電流產生擾動,由于集電極電流與基極電流成比例關系,則集電極電流(輸出回路電流)也會發生擾動,這樣,這個輸出回路電流中就有被輸入交流信號影響的擾動信號,我們要的就是輸出回路這個被基極擾動電流控制的集電極擾動的信號(輸出交流信號),這個輸出回路(集電極-發射極)擾動的信號比輸入(基極)擾動信號大,這就是放大,也可以說,放大其實是輸出回路電流受輸入信號的控制。但是,不管怎樣擾動,總體上是不能改變三個電極電流的方向的。 如果直流工作點設置合理時,那個擾動信號就與輸入交流小信號成比例關系,而且又比輸入信號大,我們要的就是這個效果。 (2)交流信號放大問題,共射極、共集電極、共基極電路的作用、優點和缺點是什么?如何克服電路的非線性?為什么共射--共基電路能擴展頻帶?為什么共集電極放大電路要放在多級放大電路的最后一級?多級放大電路的輸入級有什么要求?人們在集成電路中設計電流源的目的是什么?它的作用是什么?如何克服直接耦合帶來的零點漂移?為什么要設計成深負反饋?其優點和問題是什么?深負反饋自激的原因是什么?什么是電路的結構性相移?什么是電路的附加相移?什么情況下電路輸出信號與輸入信號之間出現附加相移?等等。 (3)集成運算放大器,為了克服半導體器件的非線性問題(不同幅度信號的放大倍數不一樣),人們有意制成了高增益的集成運算放大器,外接兩個電阻就構成了同相或反向比例放大電路,這時整個電路的電壓放大倍數就近似與半導體特性無關了(深負反饋條件下),放大倍數只與外接的兩個電阻有關,而電阻材料的溫度特性比半導體材料好,同時線性特性也改善了。在計算的時候注意運用“虛短”和“虛斷”就行了,模電學到這里那就太簡單了,所以,如果不考慮成本時誰還會用三極管分立元件組成的放大電路,還得調直流工作點。集成運算放大器的其它應用還很多,如有源濾波器、信號產生電路等。 負反饋自激振蕩與正弦波產生電路的區別 負反饋自激振蕩是由于某個未知頻率信號在反饋環路中產生了額外的180度的附加相移,負反饋電路對這個頻率信號來講就變成了正反饋,同時,對這個頻率信號的環路增益又大于1,這種情況下,負反饋電路就自激了(對其它頻率信號,此電路還是負反饋)。而正弦波振蕩電路是人們有意引入的正反饋,可以說對無數個頻率信號都是正反饋,既然這樣,環路中就不用有附加相移了,但是,這樣的信號太多了,所以,人們需要在反饋環路中設計一個選頻電路來選擇某一個頻率信號,當然,對被選取的信號來講,這個選頻電路就不需要有額外相移了。 以上大致總結了一些問題,僅供參考。 為了從全局了解這門課,以及更容易學好這門課,建議參見我主頁上我寫的文章“模擬電子技術基礎課程新的目錄”,希望對大家有幫助。 來源|作者:知乎|李澤光
免責聲明:本文內容由21ic獲得授權后發布,版權歸原作者所有,本平臺僅提供信息存儲服務。文章僅代表作者個人觀點,不代表本平臺立場,如有問題,請聯系我們,謝謝!
玩轉嵌入式
三極管
工作原理
-
題目:獲取0x12345678各個字節。
方法一:結構&聯合
#include typedef unsigned int uint32_t; typedef unsigned char uint8_t; union bit32_data
{ uint32_t data; struct { uint8_t byte0; uint8_t byte1; uint8_t byte2; uint8_t byte3;
}byte;
}; int main(void) { union bit32_data num;
num.data = 0x12345678; printf("byte0 = 0x%x\n", num.byte.byte0); printf("byte1 = 0x%x\n", num.byte.byte1); printf("byte2 = 0x%x\n", num.byte.byte2); printf("byte3 = 0x%x\n", num.byte.byte3); return 0;
}
運行結果: 這種方法的缺點是:會受到大小端的影響。只有在明確大小端的情況下可稍微用一用。以上是小端模式下的運行結果。 結構&聯合法不僅僅是在獲取數據各個字節有用,在數據協議、數據位拆分等方面很實用,之后再做更詳細的分享。
方法二:移位操作
#include #define GET_LOW_BYTE0(x) ((x >> 0) & 0x000000ff) /* 獲取第0個字節 */ #define GET_LOW_BYTE1(x) ((x >> 8) & 0x000000ff) /* 獲取第1個字節 */ #define GET_LOW_BYTE2(x) ((x >> 16) & 0x000000ff) /* 獲取第2個字節 */ #define GET_LOW_BYTE3(x) ((x >> 24) & 0x000000ff) /* 獲取第3個字節 */ int main(void) { unsigned int a = 0x12345678; printf("byte0 = 0x%x\n", GET_LOW_BYTE0(a)); printf("byte1 = 0x%x\n", GET_LOW_BYTE1(a)); printf("byte2 = 0x%x\n", GET_LOW_BYTE2(a)); printf("byte3 = 0x%x\n", GET_LOW_BYTE3(a)); return 0;
}
運行結果: 這也是獲取數據各個字節的最常用也最有效的方法。這種類似的位操作在嵌入式方面應用得很多,比如在LCD操作中表示像素顏色值、FLASH的操作等方面都有用到。位操作推薦文章: 《C語言位域(位段)詳解》
免責聲明:本文內容由21ic獲得授權后發布,版權歸原作者所有,本平臺僅提供信息存儲服務。文章僅代表作者個人觀點,不代表本平臺立場,如有問題,請聯系我們,謝謝!
玩轉嵌入式
字節
整數
移位操作
-
VSCode全稱Visual Studio Code,是微軟推出的一款輕量級的代碼編輯器,免費且功能強大。支持各種插件,支持幾乎所有的主流程序語言的語法高亮、代碼自動補全、括號匹配等。
Keil是一款優秀的編輯和編譯一體的嵌入式開發軟件,但其編輯器的功能一直比較弱,被眾多用戶所詬病。本篇文章將介紹使用VSCode來代替Keil實現單片機程序的編輯和編譯。
首先要安裝Keil和VSCode軟件。安裝過程不詳細介紹。
安裝完成后打開VSCode,安裝兩個插件,首先安裝C/C++插件。然后安裝Keil Assistant,該助手會在Keil的工程目錄新建一個.vscode的文件夾,并添加相關文件,使工程能被VSCode軟件所識別。 安裝完成后點擊設置,在Extension Settings菜單下,設置Keil安裝路徑。 (這里是設置Keil MDK,如果是Keil C51,設置在上面)。 設置完成后直接打開Keil的工程文件即可。 打開后就可以體驗VSCode強大的編輯器功能了,比Keil本身的要好用多了。 將鼠標放在工程目錄上,可以看到編譯、下載的圖標: 編譯、下載與Keil中的一樣,因為它就是調用的Keil的編譯器和下載驅動。 不足的地方就是該插件只支持編譯和下載,不支持在線調試,不太完善。 還有另外一種方式可以完全擺脫Keil,使用VSCode編寫單片機程序。 需要安裝gcc、OpenOCD等多個插件,配置也比較復雜,感興趣的朋友可以自己探索一下。
玩轉嵌入式
Keil
VSCode
-
通常的PCB設計電流都不會超過10 A,甚至5 A。尤其是在家用、消費級電子中,通常PCB上持續的工作電流不會超過2 A。但是最近要給公司的產品設計動力走線,持續電流能達到80 A左右,考慮瞬時電流以及為整個系統留下余量,動力走線的持續電流應該能夠承受100 A以上。
那么問題就來了,怎么樣的PCB才能承受住100 A的電流?
方法1:PCB上走線
要弄清楚PCB的過流能力,我們首先從PCB結構下手。以雙層PCB為例,這種電路板通常是三層式結構:銅皮、板材、銅皮。銅皮也就是PCB中電流、信號要通過的路徑。根據中學物理知識可以知道一個物體的電阻與材料、橫截面積、長度有關。由于我們的電流是在銅皮上走,所以電阻率是固定的。橫截面積可以看作銅皮的厚度,也就是PCB加工選項中的銅厚。通常銅厚以OZ來表示,1 OZ的銅厚換算過來就是35 um,2 OZ是70 um,依此類推。那么可以很輕易地得出結論:在PCB上要通過大電流時,布線就要又短又粗,同時PCB的銅厚越厚越好。
實際在工程上,對于布線的長度沒有一個嚴格的標準。工程上通常會用:銅厚/溫升/線徑,這三個指標來衡量PCB板的載流能力。
以下兩個表可以參考:
從表中可以大約知道1 OZ銅厚的電路板,在10°溫升時,100 mil (2.5 mm) 寬度的導線能夠通過4.5 A的電流。并且隨著寬度的增加,PCB載流能力并不是嚴格按照線性增加,而是增加幅度慢慢減小,這也是和實際工程里的情況一致。如果提高溫升,導線的載流能力也能夠得到提高。
通過這兩個表,能得到的PCB布線經驗是:增加銅厚、加寬線徑、提高PCB散熱能夠增強PCB的載流能力。
那么如果我要走100 A的電流,我可以選擇4 OZ的銅厚,走線寬度設置為15 mm,雙面走線,并且增加散熱裝置,降低PCB的溫升,提高穩定性。
方法2:接線柱
除了在PCB上走線之外,還可以采用接線柱的方式走線。 在PCB上或產品外殼上固定幾個能夠耐受100 A的接線柱如:表貼螺母、PCB接線端子、銅柱等。然后采用銅鼻子等接線端子將能承受100 A的導線接到接線柱上。這樣大電流就可以通過導線來走。
方法3:定做銅排
甚至,還可以定做銅排。使用銅排來走大電流是工業上常見的做法,例如變壓器,服務器機柜等應用都是用銅排來走大電流。
附銅排載流能力表:
方法4:特殊工藝
另外還有一些比較特殊的PCB工藝,國內不一定能找得到加工的廠家。英飛凌就有一種PCB,采用3 層銅層設計,頂層和底層是信號布線層,中間層是厚度為1.5 mm的銅層,專門用于布置電源,這種PCB可以輕易做到小體積過流100 A以上。
免責聲明:本文內容由21ic獲得授權后發布,版權歸原作者所有,本平臺僅提供信息存儲服務。文章僅代表作者個人觀點,不代表本平臺立場,如有問題,請聯系我們,謝謝!
玩轉嵌入式
PCB
大電流
-
很二很二的單片機
單片機比較二,啥都不認識,只認識0和1,即所謂的“二進制”,由于把0和1認到了極致,所以單片機能用0和1干許多事情。人在看二進制數據時,比較麻煩。比如二進制110 0100,不能一眼看出是十進制100,這無疑降低了效率。為了提高效率,人類發明了很多單位,比如bit、Byte、KB、MB等。 今天的文章很簡單,但是很重要。但是把簡單的事情做到極致,人人都可以是單片機。
什么是bit位
二進制只有0和1兩個狀態,非此即彼,其中的任意一個狀態就是一個bit位。bit位是數據存儲的最小單位。一個bit有0和1兩種狀態;兩個bit就有00、01、10和11四個狀態;以此類推,三個bit就有000-111等八個狀態,這個規律符合2^n。所以計算機能表示很多種狀態。 單片機都有位數之分,比如STM8S為8位單片機,表示一次最多能處理8位數據;STM32F103為32位單片機,一次最多可以處理32位數據。
什么是字節Byte
數據比較多時,二進制看起來比較麻煩。八個bit位的數據構成一個字節Byte,所以一個字節有8位,即
玩轉嵌入式
單片機
-
對于學習過51單片機的同學來說,一般使用RTC功能,一般都會使用51單片機+DS1302的方案,在STM32單片機中,因為STM32單片機自帶RTC模塊,所以我們只需要使用一個STM32即可,不需要外掛實時時鐘芯片了。
RTC實時時鐘簡介
"RTC"是Real Time Clock 的簡稱,意為實時時鐘。
STM32提供了一個秒中斷源和一個鬧鐘中斷源,修改計數器的值可以重新設置系統當前的時間和日期。
STM32的RTC外設,實質是一個掉電后還能繼續運行的定時器,通過配置,可以讓它準確地每秒鐘中斷一次。
所謂掉電,是指電源VDD斷開的情況下,為了RTC外設掉電可以繼續運行,必須給STM32芯片通過VBAT引腳接上外部3.3V供電。
當主電源VDD有效時,由VDD給RTC外設供電。
當VDD掉電后,由VBAT給RTC外設供電。
無論由什么電源供電,RTC中的數據始終都保存在屬于RTC的備份域中,如果主電源和VBAT都掉電,那么備份域中保存的所有數據都將丟失。(備份域除了RTC模塊的寄存器,還有42個16位的寄存器可以在VDD掉電的情況下保存用戶程序的數據,系統復位或電源復位時,這些數據也不會被復位)。
上圖來自于《STM32中文參考手冊》
備份寄存器是42個16位的寄存器,可用來存儲84個字節的用戶應用程序數據。他們處在備份域里,當VDD電源被切斷,他們仍然由VBAT維持供電,數據不會丟失,所以我們可以使用這些寄存器預存一些運行狀態信息。
比如我們可以在RTC初始化之后,對BKP_DR1寄存器寫入一個值,比如0x5050,然后在 RTC_Init(void)函數開始處,先讀取并判斷BKP_DR1寄存器的值是否為0x5050,進而可以知道系統的RTC是否有VBAT外部電源維持供電。
STM32時鐘系統
參考《STM32中文參考手冊》中對STM32的時鐘系統的詳細說明。
RTC的時鐘源可以來源于3個渠道:
來自于低速外部時鐘LSE,也就是外部的晶振,一般選用32.768KHz
來自于高速外部時鐘HSE的128分頻,即HSE/128
來自于低速內部時鐘LSI
使用HSE分頻時鐘或者LSI的時候,在主電源VDD掉電的情況下,這兩個時鐘來源都會受到影響,因此沒法保證RTC正常工作(在有自動校準功能并對時鐘的精度要求不是很高的情況下,還是可以使用HSE分頻時鐘作為RTC時鐘的),所以RTC一般都選用低速外部時鐘LSE,晶振的頻率為實時時鐘模塊中常用的32.768KHz,因為32768 = 2^15,分頻容易實現1秒的時鐘頻率,所以被廣泛應用到RTC模塊。
在配置RTC模塊的時鐘時,把輸入的32768Hz的RTCCLK進行32768分頻得到實際驅動計數器的時鐘TR_CLK = RTCCLK/37768 = 1Hz,計時周期為1秒,計時器在TR_CLK的驅動下計數,即每秒計數器RTC_CNT的值加1。
RTC時間設置
RTC時間設置就是對RTC計數器寄存器RTC_CNT進行設置。
該寄存器由兩個16位的寄存器RTC_CNTH和RTC_CNTL組成,總共32位,用來記錄秒鐘值。
理論上可以計算2^32 = 4,294,967,296? s,大約136年。
所以設置RTC時間或者獲取RTC時間,就是設置RTC_CNT寄存器或者獲取RTC_CNT寄存器的值。
所以封裝的設置時鐘的函數RTC_Set(),就是求得設定的時間與1970年1月1日 00:00:00之間的秒數,然后通過RTC_SetCounter()函數,寫入RTC_CNT寄存器即可。
注意:
每次操作RTC_CNT時應該要使能PWR 和 BKP 時鐘,允許訪問BKP域,否則會操作失敗。
/* Enable PWR and BKP clocks*/RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE);/* Allow access to BKP Domain */PWR_BackupAccessCmd(ENABLE); /* Wait until last write operation on RTC registers has finished*/RTC_WaitForLastTask();/* 修改當前RTC計數寄存器內容 */RTC_SetCounter(Cnt);/* Wait until last write operation on RTC registers has finished*/RTC_WaitForLastTask();
RTC鬧鐘
在實際應用中,我們有時會用到定時鬧鐘功能,我們簡單的可以在main函數的while循環中,通過比對當前時間和設定時間值是否相等進行判斷是否定時時間到,比如:
//主循環 while(1) { times++; if(t!=calendar.sec) { t=calendar.sec; NowHour = calendar.hour; NowMinute = calendar.min; NowSecond = calendar.sec; if(NowHour==AlarmHour&&NowMinute==AlarmMinute&&NowSecond==AlarmSecond) { //執行預定動作 } } }
除了上面的方法外,我們還可以使用RTC的鬧鐘中斷來實現。
使能RTC_IT_ALR中斷,并設置鬧鐘時間:
鬧鐘中斷響應
在主電源VDD有效的情況下(待機),RTC還可以配置鬧鐘事件使STM32退出待機模式。
結果展示
RTC_Init(void)函數中,設置初始化時間如下:
程序會在串口助手中,每秒鐘輸出一條時間信息,40S后執行一次鬧鐘中斷。
STM32串口發送數據和接收數據方式總結!
STM32配置SPI通訊功能
還在敲代碼? 來看看如何自動生成"狀態機"代碼
SPI怎么玩?搞懂時序,運用自如
什么是DMA? STM32如何配置DMA?
STM32基礎分析——PWM配置
STM32單片機:獨立看門狗、窗口看門狗的配置
免責聲明:本文內容由21ic獲得授權后發布,版權歸原作者所有,本平臺僅提供信息存儲服務。文章僅代表作者個人觀點,不代表本平臺立場,如有問題,請聯系我們,謝謝!
玩轉嵌入式
STM32
51單片機
-
隨著公司積累大量數據并尋找合適的技術進行分析和利用,人工智能(AI)逐漸成為主流。這就是為什么Gartner預測到2021年80%的新興技術將擁有AI基礎。
隨著預測分析,機器學習和其他數據科學的趨勢已經開始,營銷人員需要開始關注如何利用這些技術來形成以數據為驅動力的營銷策略。考慮到這一點,我們詢問了AI行業專家,為什么營銷領導者需要開始考慮AI,以及一些最好的開源AI框架來保持關注。
這里有6個最受歡迎的創新開源AI框架。
1. TensorFlow
TensorFlow是一個由工具,庫和資源組成的生態系統,許多受歡迎的公司(如Airbnb,eBay,DropBox等)都在使用它。TensorFlow旨在簡化和簡化機器學習算法的復雜性以簡化開發。使用視覺模型和流程圖,開發人員和數據科學家可以快速創建神經網絡和其他機器學習模型來利用數據。例如,Airbnb正在使用TensorFlow對公寓列表中的照片進行分類,以確保它們準確代表特定的空間。
2. 亞馬遜 SageMaker Neo
亞馬遜最近將其機器學習平臺的功能Amazon SageMaker Neo開源,作為服務產品。新發布的Neo-AI項目代碼將使AI開發人員能夠訓練機器學習模型并在云中的任何地方運行它們。Neo-AI項目針對需要快速和低延遲預測的邊緣計算設備和物聯網(IoT)傳感器進行了優化。
例如,專門從事數字娛樂產品的公司先鋒公司(Pioneer Corp)使用Amazon SageMaker Neo進行實時圖像檢測和汽車內攝像頭的分類。同樣,野村綜合研究所(NRI)正在使用Amazon SageMaker Neo來檢測便利商店,機場和其他企業中安裝的相機中的物體,以優化運營。
3. Scikit-learn
Scikit-learn是一個基于Python的開源機器學習庫,專注于數據挖掘和分析。它建立在NumPy,SciPy和matplotlib之上,并具有精選的一組高質量的機器學習模型,可用于最受歡迎的用例。Morgan和Evernote等知名品牌使用Scikit-learn進行預測分析,個性化推薦和其他數據驅動的任務。
4. Microsoft認知工具包
Microsoft認知工具包(CNTK)是一個開源的深度學習框架。 CNTK可以以各種語言的庫形式包含在項目中,也可以通過自己的稱為BrainScript的模型描述語言用作獨立的機器學習工具。 Bing,Cortana和其他品牌的商業級工具包使用的海量數據集需要可擴展且高度優化的機器學習平臺。
5. Theano Theano是與NumPy緊密集成的深度學習Python庫。這意味著其主要用例是使用相對簡單的Python腳本定義和評估復雜的數學表達式,同時利用高級計算來優化性能。即便如此,Theano仍被認為是低級框架,許多品牌選擇使用在其之上構建的框架如Keras或Blocks。
6. Keras Keras是可以在TensorFlow,Microsoft Cognitive Toolkit和Theano之上運行的高級機器學習API。它的易用性和對開發人員體驗的關注使Keras成為快速制作新應用程序原型的首選。 Netflix,Uber和Yelp等許多品牌以及規模較小的創業公司已將Keras集成到其核心產品和服務中。
免責聲明:本文內容由21ic獲得授權后發布,版權歸原作者所有,本平臺僅提供信息存儲服務。文章僅代表作者個人觀點,不代表本平臺立場,如有問題,請聯系我們,謝謝!
玩轉嵌入式
人工智能
AI
開發框架
-
隨著我們工程化經驗的增加,不知不覺的我們就會關心到這個問題,模塊化,模塊設計就顯現出來,那到底什么是模塊化呢?
這不叫模塊化
我相信在很多時候,我們剛開始從零開始接手一個項目的時候,編碼之前總想著要實現什么的功能需要的模塊,然后要程序 模塊化,這種思想是值得認同的,但往往我們并沒有做到真正的模塊化。 例如在一個需要很多外設接口,一般需要硬件初始化,相關配置,中斷服務程序,輸入輸出,邏輯處理等功能,我們的做法可能就是把代碼分布到多個文件和目錄里面,然后把這些目錄或者文件取名 xxxModule。 甚至把這些目錄分放在不同的倉庫目錄里面,結果隨著編碼的增加,發現好多小功能都重復了,或者本可以寫在一起的函數并沒有放在一起,導致我們的代碼思想不是很流暢,這樣做會誤導我們,甚至整個項目實現的思路。 究其原因這是因為我們其實并不理解什么叫做 模塊,而僅僅是膚淺的把代碼切割開來,分放在不同的位置,雖然這確實達到了部分模塊化的目的,但是也會制造一些不必要的麻煩。
什么是真正的模塊化?
真正的模塊化,并不是簡單文本意義上的,而是與邏輯相關的有邏輯意義的。一個模塊應該像一個集成電路芯片,我們能見到能使用的都很清晰,它定義了良好的輸入和輸出。 模塊是可能分開地被編寫的單位。這使他們可再用和允許廣泛人員同時協作、編寫及研究不同的模塊。 實際上,編程語言已經為我們提供了一種很好的模塊化方法,它的名字叫做 函數。每一個函數都有明確的 輸入(參數)和輸出(返回值),同一個文件里可以包含多個函數,所以你其實根本不需要把代碼分開在多個文件或者目錄里面,同樣可以完成代碼的模塊化。 按照函數這個原則,我可以把代碼全都寫在同一個文件里,卻仍然是非常模塊化的代碼,是不是覺得與之前的想法不一樣? 是的,軟件編程模塊是一套一致而互相有緊密關聯的軟件組織, 每個模塊完成一個特定的子功能,所有的模塊按某種方法組裝起來,成為一個整體,完成整個系統所要求的功能,這就是真正的模塊化。
怎么模塊化?
我們知道了模塊化的原則與道理之后,就可以按照這個思路去開發項目了,想要達到很好的模塊化,你需要做到以下幾點。我們從實現角度來說。
避免寫太長的函數
一眼望去,如果一個函數的代碼你電腦一頁都看不完,那肯定是冗長了或者不符合模塊化編程了。不需要滾屏就可以看得到全部的函數內容,那對我們的理解也有幫助。 一般來說一個函數最好不要超過40行代碼(當然這個不是死規定,只是一個經驗建議而已),如果寫的函數太大了,就應該把它拆分成幾個更小的函數。 也許你會說到,這很難辦到,邏輯很多或者很多判斷條件的時候,40行往往不夠吧,那么其實我們也需要考慮到函數里面一些復雜的部分,是不是可以提取出來,單獨寫一個小函數,再從原來的函數里面調用。 另外函數層級也不要太多,比如一個函數長成這樣:
1void function(void* para) 2{ 3 if (getOS().equals("MacOS")) { 4 a(); 5 if(getOS().equals("AndroidOS")){ 6 b(); 7 if(getOS().equals("flymeOS")){ 8 c(); 9 } 10 } 11 } 12}
我們看到這個函數由于很多的判斷,函數層級已經超過4層了,這其實對我們的理解很不利,另一方面,一些邏輯變化也會導致我們的更改很麻煩,費腦子。
每個函數只做一件簡單的事情
有些人喜歡寫一些 通用函數,一般我都放在 publicModule里面,既可以實現這個工又可以實現那個功能,它的內部依據某些變量或者是某些條件,來選擇這個函數所要實現的小功能。 比如,寫出這樣的函數:
1void function() { 2 if (getOS().equals("MacOS")) { 3 a(); 4 } else { 5 b(); 6 } 7 8 c(); 9 10 if (getOS().equals("MacOS")) { 11 d(); 12 } else { 13 e(); 14 } 15}
這個函數,是想表達根據系統是否為MacOS,從而來做不同的事情。從這個函數可以很容易的看出,其實只有函數c()是兩種系統共有的,而其它的函數 a(), b(), d(), e()都屬于不同的分支。 但是這種復用的寫法其實是很不利的。如果一個函數可能實現2個功能,并且它們之間 共同點少于它們的不同點,那我們最好就寫兩個不同的函數,否則這個函數的邏輯就不會很清晰,容易出現錯誤。 不要害怕,函數簡單點不丟人,我們不需要炫技。 好了,根據上面的說法,這個函數可以改寫成兩個函數:
1void funMacOS() { 2 a(); 3 c(); 4 d(); 5}
和
1void funOther() { 2 b(); 3 c(); 4 e(); 5}
如果我們發現兩件事情大部分內容相同,只有少數不同,也就是說 共同點大于它們的不同點,那就更簡單了,我們可以把相同的部分提取出去,做成一個 輔助函數。 比如,如果有個函數是這樣:
1void function() { 2 3 a(); 4 b() 5 c(); 6 7 if (getOS().equals("MacOS")) { 8 d(); 9 } else { 10 e(); 11 } 12}
其中函數 a(),b(),c()都是一樣的,只有函數 d()、e()根據系統有所不同。那么你可以把函數 a(),b(),c()提取出去,代碼如下:
1void preFun() { 2 a(); 3 b() 4 c(); 5}
然后分別寫兩個函數:
1void funMacOS() { 2 preFun(); 3 d(); 4}
和
1void funOther() { 2 preFun(); 3 e(); 4}
這樣更改之后,是不是清晰了很多,我們既共享了代碼,避免冗余,又做到了每個函數只做一件簡單的事情。這樣的代碼,邏輯就更加清晰了。
工具函數
是的,我再說一遍,每個函數只做一件簡單的事情。但是有些時候在我們的工程代碼中,我們可能會發現其實里面有很多的重復。 所以一些常用的代碼,不管它有多短或者多么的簡單,都可以提取出去做成函數,例如有些幫助函數也許就只有2行,但是我們把它封裝成一個函數的話,就能大大簡化主要函數里面的邏輯。 也許你可能會說,這些函數調用會增加代碼開銷,但隨著硬件發展以及技術變革,這已經是一種過時的觀念了。 現代的很多編譯器都能自動的把小的函數 內聯(inline)到調用它的地方,所以根本不會產生函數調用,也就不會產生任何多余的開銷。 那么我可以使用宏來代替工具函數?一行代碼搞定了,比如
1#define FillAndSendTxOptions( TRANSSEQ, ADDR, ID, LEN, TxO ) { \ 2afStatus_t stat; \ 3ZDP_TxOptions = (TxO); \ 4stat = fillAndSend( (TRANSSEQ), (ADDR), (ID), (LEN) ); \ 5ZDP_TxOptions = AF_TX_OPTIONS_NONE; \ 6return stat; \ 7}
同樣,這也許也過時了,我不想讓宏(macro)來背這個鍋,在早期的C語言編譯器里,只有宏是靜態內聯的,所以使用宏是為了達到內聯的目的。 然而能否內聯,其實并不是宏與函數的根本區別,這里我不細說了,只要記住: 應該盡量避免使用宏。如果想了解可以參考 避免這7個誤區,才能讓【宏】削鐵如泥 為了內聯而使用宏,其實是濫用了宏,這會引起各種各樣的麻煩,比如使程序難以理解,難以調試,容易出錯等等。
盡量使用局部變量和參數
我們應該盡量避免使用全局變量和類成員(class member)來傳遞信息,舉個例子:
1class A { 2 String x; 3 4 void findX() { 5 ... 6 x = ...; 7 } 8 9 void fun() { 10 findX(); 11 ... 12 print(x); 13 } 14}
首先,使用函數findX(),把一個值寫入成員x。然后,調用x的值。這樣,x就變成了findX和print之間的數據通道。 由于 x屬于class A,這樣程序就失去了模塊化的結構,與我們所說的模塊化意義不符了。兩個函數依賴于成員x,就不再有明確的輸入和輸出,而是依賴全局的數據。 函數findX和fun不再能夠離開 class A而存在,具有依賴性,并且由于類成員還有可能被其他代碼改變,這樣就會導致代碼變得復雜難以理解,函數的正確性也難以保證。 如果使用局部變量和參數來傳遞信息,那么這兩個函數就不需要依賴于某一個class,不易出錯,代碼如下:
1String findX() { 2 ... 3 x = ...; 4 return x; 5 } 6 void foo() { 7 String x = findX(); 8 print(x); 9 }
總結
模塊化是指解決一個復雜問題時,自頂向下,逐層把系統劃分成若干模塊的過程,深入理解模塊化,什么是真正的模塊化,那么我們才能夠事半功倍。 如果你喜歡我的文章表達的思維,轉發分享是對我的厚愛,期待您的點贊在看,下一期,再見!
免責聲明:本文內容由21ic獲得授權后發布,版權歸原作者所有,本平臺僅提供信息存儲服務。文章僅代表作者個人觀點,不代表本平臺立場,如有問題,請聯系我們,謝謝!
玩轉嵌入式
代碼
模塊化
-
素材來源:最后一個bug
狀態(State) 指的是對象在其生命周期中的一種狀況,處于某個特定狀態中的對象必然會滿足某些條件、執行某些動作或者是等待某些事件。
事件(Event) 指的是在時間和空間上占有一定位置,并且對狀態機來講是有意義的那些事情。事件通常會引起狀態的變遷,促使狀態機從一種狀態切換到另一種狀態。
轉換(Transition) 指的是兩個狀態之間的一種關系,表明對象將在第一個狀態中執行一定的動作,并將在某個事件發生- 同時某個特定條件滿足時進入第二個狀態。
動作(Action) 指的是狀態機中可以執行的那些原子操作,所謂原子操作指的是它們在運行的過程中不能被其他消息所中斷,必須一直執行下去。
二、手工編寫狀態機
與其他常用的設計模式有所不同,程序員想要在自己的軟件系統中加入狀態機時,必須再額外編寫一部分用于邏輯控制的代碼,如果系統足夠復雜的話,這部分代碼實現和維護起來還是相當困難的。在實現有限狀態機時,使用switch語句是最簡單也是最直接的一種方式,其基本思路是為狀態機中的每一種狀態都設置一個case分支,專門用于對該狀態進行控制。下面的代碼示范了如何運用switch語句,來實現圖1中所示的狀態機:
switch (state) {
// 處理狀態Opened的分支
case (Opened): {
// 執行動作Open
open();
// 檢查是否有CloseDoor事件
if (closeDoor()) {
// 當前狀態轉換為Closed
changeState(Closed)
}
break;
}
// 處理狀態Closed的分支
case (Closed): {
// 執行動作Close
close();
// 檢查是否有OpenDoor事件
if (openDoor()) {
// 當前狀態轉換為Opened
changeState(Opened);
}
// 檢查是否有LockDoor事件
if (lockDoor()) {
// 當前狀態轉換為Locked
changeState(Locked);
}
break;
}
// 處理狀態Locked的分支
case (Locked): {
// 執行動作Lock
lock();
// 檢查是否有UnlockDoor事件
if (unlockDoor()) {
// 當前狀態轉換為Unlocked
changeState(Unlocked);
}
break;
}
// 處理狀態Unlocked的分支
case (Unlocked): {
// 執行動作Unlock
unlock();
// 檢查是否有LockDoor事件
if (lockDoor()) {
// 當前狀態轉換為Locked
changeState(Locked)
}
// 檢查是否有OpenDoor事件
if (openDoor()) {
// 當前狀態轉換為Opened
changeSate(Opened);
}
break;
}
}
使用switch語句實現的有限狀態機的確能夠很好地工作,但代碼的可讀性并不十分理想,主要原因是在實現狀態之間的轉換時,檢查轉換條件和進行狀態轉換都是混雜在當前狀態中來完成的。例如,當城門處于Opened狀態時,需要在相應的case中調用closeDoor()函數來檢查是否有必要進行狀態轉換,如果是的話則還需要調用changeState()函數將當前狀態切換到Closed。顯然,如果在每種狀態下都需要分別檢查多個不同的轉換條件,并且需要根據檢查結果讓狀態機切換到不同的狀態,那么這樣的代碼將是枯燥而難懂的。從代碼重構的角度來講,此時更好的做法是引入checkStateChange()和performStateChange()兩個函數,專門用來對轉換條件進行檢查,以及激活轉換時所需要執行的各種動作。這樣一來,程序結構將變得更加清晰:
switch (state) { // 處理狀態Opened的分支 case (Opened): { // 執行動作Open open(); // 檢查是否有激發狀態轉換的事件產生 if (checkStateChange()) { // 對狀態機的狀態進行轉換 performStateChange();
} break;
} // 處理狀態Closed的分支 case (Closed): { // 執行動作Close close(); // 檢查是否有激發狀態轉換的事件產生 if (checkStateChange()) { // 對狀態機的狀態進行轉換 performStateChange();
} break;
} // 處理狀態Locked的分支 case (Locked): { // 執行動作Lock lock(); // 檢查是否有激發狀態轉換的事件產生 if (checkStateChange()) { // 對狀態機的狀態進行轉換 performStateChange();
} break;
} // 處理狀態Unlocked的分支 case (Unlocked): { // 執行動作Lock unlock(); // 檢查是否有激發狀態轉換的事件產生 if (checkStateChange()) { // 對狀態機的狀態進行轉換 performStateChange();
} break;
}
}
但checkStateChange()和performStateChange()這兩個函數本身依然會在面對很復雜的狀態機時,內部邏輯變得異常臃腫,甚至可能是難以實現。
在很長一段時期內,使用switch語句一直是實現有限狀態機的唯一方法,甚至像編譯器這樣復雜的軟件系統,大部分也都直接采用這種實現方式。但之后隨著狀態機應用的逐漸深入,構造出來的狀態機越來越復雜,這種方法也開始面臨各種嚴峻的考驗,其中最令人頭痛的是如果狀態機中的狀態非常多,或者狀態之間的轉換關系異常復雜,那么簡單地使用switch語句構造出來的狀態機將是不可維護的。
三、自動生成狀態機
為實用的軟件系統編寫狀態機并不是一件十分輕松的事情,特別是當狀態機本身比較復雜的時候尤其如此,許多有過類似經歷的程序員往往將其形容為"毫無創意"的過程,因為他們需要將大量的時間與精力傾注在如何管理好狀態機中的各種狀態上,而不是程序本身的運行邏輯。作為一種通用的軟件設計模式,各種軟件系統的狀態機之間肯定會或多或少地存在著一些共性,因此人們開始嘗試開發一些工具來自動生成有限狀態機的框架代碼,而在Linux下就有一個挺不錯的選擇──FSME(Finite State Machine Editor)。
FSME是一個基于Qt的有限狀態機工具,它能夠讓用戶通過圖形化的方式來對程序中所需要的狀態機進行建模,并且還能夠自動生成用C++或者Python實現的狀態機框架代碼。下面就以城門的狀態機為例,來介紹如何利用FSME來自動生成程序中所需要的狀態機代碼。
3.1狀態機建模
首先運行fsme命令來啟動狀態機編輯器,然后單擊工具欄上的"New"按鈕來創建一個新的狀態機。FSME中用于構建狀態機的基本元素一共有五種:事件(Event)、輸入(Input)、輸出(Output)、狀態(State)和轉換(Transition),在界面左邊的樹形列表中可以找到其中的四種。
轉換建模
狀態轉換是整個建模過程中最重要的一個部分,它用來定義有限狀態機中的一個狀態是如何切換到另一個狀態的。例如,當用來控制城門的狀態機處于Opened狀態時,如果此時有Close事件產生,那么狀態機的當前狀態將切換到Closed狀態,這樣一個完整的過程在狀態機模型中可以用closeDoor這樣一個轉換來進行描述。
要在FSME中添加這樣一個轉換,首先需要在界面左邊的樹形列表中選擇"States"下的"Opened"項,然后按下鍵盤上的Insert鍵來添加一個新的轉換,接著在右下角的"Name"文本框中輸入轉換的名字"closeDoor",在"Condition"文本框中輸入"Close"表明觸發該轉換的條件是事件Close的產生,在"Target"下拉框中選擇"Closed"項表明該轉換發生后狀態機將被切換到Closed狀態,最后再單擊"Apply"按鈕,一個新的狀態轉換關系就定義好了。用同樣的辦法可以添加狀態機所需要的所有轉換。
3.2 生成狀態機框架
使用FSME不僅能夠進行可視化的狀態機建模,更重要的是它還可以根據得到的模型自動生成用C++或者Python實現的狀態機框架。首先在FSME界面左邊的樹形列表中選擇"Root"項,然后在右下角的"Name"文本框中輸入狀態機的名字"DoorFSM",再從"Initial State"下拉列表中選擇狀態"Opened"作為狀態機的初始化狀態。
在將狀態機模型保存為door.fsm文件之后,使用下面的命令可以生成包含有狀態機定義的頭文件:
[xiaowp@linuxgam code]$ fsmc door.fsm -d -o DoorFSM.h
進一步還可以生成包含有狀態機實現的框架代碼:
[xiaowp@linuxgam code]$ fsmc door.fsm -d -impl DoorFSM.h -o DoorFSM.cpp
如果想對生成的狀態機進行驗證,只需要再手工編寫一段用于測試的代碼就可以了:
/*
* TestFSM.cpp
* 測試生成的狀態機框架
*/ #include "DoorFSM.h" int main() {
DoorFSM door;
door.A(DoorFSM::Close);
door.A(DoorFSM::Lock);
door.A(DoorFSM::Unlock);
door.A(DoorFSM::Open);
}
有限狀態機是由事件來進行驅動的,在FSME生成的狀態機框架代碼中,方法A()可以被用來向狀態機發送相應的事件,從而提供狀態機正常運轉所需要的"動力"。狀態機負責在其內部維護一個事件隊列,所有到達的事件都會先被放到事件隊列中進行等候,從而能夠保證它們將按照到達的先后順序被依次處理。在處理每一個到達的事件時,狀態機都會根據自己當前所處的狀態,檢查與該狀態對應的轉換條件是否已經被滿足,如果滿足的話則激活相應的狀態轉換過程。
使用下面的命令能夠將生成的狀態機框架和測試代碼編譯成一個可執行文件:
[xiaowp@linuxgam code]$ g++ DoorFSM.cpp TestFSM.cpp -o fsm
由于之前在用fsmc命令生成狀態機代碼時使用了-d選項,生成的狀態機框架中會包含一定的調試信息,包括狀態機中每次狀態轉換時的激活事件、轉換前的狀態、所經歷的轉換、轉換后的狀態等,如下所示:
[xiaowp@linuxgam code]$ ./fsm
DoorFSM:event:'Close' DoorFSM:state:'Opened' DoorFSM:transition:'closeDoor' DoorFSM:new state:'Closed' DoorFSM:event:'Lock' DoorFSM:state:'Closed' DoorFSM:transition:'lockDoor' DoorFSM:new state:'Locked' DoorFSM:event:'Unlock' DoorFSM:state:'Locked' DoorFSM:transition:'unlockDoor' DoorFSM:new state:'Unlocked' DoorFSM:event:'Open' DoorFSM:state:'Unlocked' DoorFSM:transition:'openDoor' DoorFSM:new state:'Opened'
3.3 定制狀態機
目前得到的狀態機已經能夠響應來自外部的各種事件,并適當地調整自己當前所處的狀態,也就是說已經實現了狀態機引擎的功能,接下來要做的就是根據應用的具體需求來進行定制,為狀態機加入與軟件系統本身相關的那些處理邏輯。在FSME中,與具體應用相關的操作稱為輸出(Output),它們實際上就是一些需要用戶給出具體實現的虛函數,自動生成的狀態機引擎負責在進入或者退出某個狀態時調用它們。
仍然以控制城門的那個狀態機為例,假設我們希望在進入每個狀態時都添加一部分處理邏輯。首在FSME界面左邊的樹形列表選擇"Outputs"項,然后按下鍵盤上的Insert鍵來添加一個新的輸出,接著在右下方的"Name"文本框中輸入相應的名稱,再單擊"Apply"按鈕,一個新的輸出就創建好了,如圖7所示。用同樣的辦法可以添加狀態機所需要的所有輸出。
圖7 添加輸出
當所有的輸出都定義好之后,接下來就可以為狀態機中的每個狀態綁定相應的輸出。首先在FSME界面左側的"States"項中選擇相應的狀態,然后從右下角的"Available"列表框中選擇與該狀態對應的輸出,再單擊"<"按鈕將其添加到"In"列表中,如圖8所示。用同樣的辦法可以為狀態機中的所有狀態設置相應的輸出,同一個狀態可以對應有多個輸出,其中In列表中的輸出會在進入該狀態時被調用,而Out列表中的輸出則會在退出該狀態時被調用,輸出調用的順序是與其在In或者Out列表中的順序相一致的。
圖8 為狀態設置輸出
由于對狀態機模型進行了修改,我們需要再次生成狀態機的框架代碼,不過這次不需要加上-d參數:
[xiaowp@linuxgam code]$ fsmc door.fsm -o DoorFSM.h
[xiaowp@linuxgam code]$ fsmc door.fsm -d -impl DoorFSM.h -o DoorFSM.cpp
我們在新的狀態機模型中添加了enterOpend、enterClosed、enterLocked和enterUnlocked四個輸出,因此生成的類DoorFSM中會包含如下幾個純虛函數:
virtual void enterOpened() = 0; virtual void enterLocked() = 0; virtual void enterUnlocked() = 0; virtual void enterClosed() = 0;
顯然,此時生成的狀態機框架不能夠再被直接編譯了,我們必須從類DoorFSM派生出一個子類,并提供對這幾個純虛函數的具體實現:
/*
* DoorFSMLogic.h
* 狀態機控制邏輯的頭文件
*/ #include "DoorFSM.h" class DoorFSMLogic : public DoorFSM
{ protected: virtual void enterOpened(); virtual void enterLocked(); virtual void enterUnlocked(); virtual void enterClosed();
};
正如前面所提到過的,這幾個函數實際上代表的正是應用系統的處理邏輯,作為例子我們只是簡單地輸出一些提示信息:
/*
* DoorFSMLogic.cpp
* 狀態機控制邏輯的實現文件
*/ #include "DoorFSMLogic.h" #include void DoorFSMLogic::enterOpened()
{ std::cout << "Enter Opened state." << std::endl;
} void DoorFSMLogic::enterClosed()
{ std::cout << "Enter Closed state." << std::endl;
} void DoorFSMLogic::enterLocked()
{ std::cout << "Enter Locked state." << std::endl;
} void DoorFSMLogic::enterUnlocked()
{ std::cout << "Enter Unlocked state." << std::endl;
}
同樣,為了對生成的狀態機進行驗證,我們還需要手工編寫一段測試代碼:
/*
* TestFSM.cpp
* 測試狀態機邏輯
*/ #include "DoorFSMLogic.h" int main() {
DoorFSMLogic door;
door.A(DoorFSM::Close);
door.A(DoorFSM::Lock);
door.A(DoorFSM::Unlock);
door.A(DoorFSM::Open);
}
使用下面的命令能夠將生成的狀態機框架和測試代碼編譯成一個可執行文件:
[xiaowp@linuxgam code]$ g++ DoorFSM.cpp DoorFSMLogic.cpp TestLogic.cpp -o logic
運行結果如下所示:
[xiaowp@linuxgam code]$ ./logic
Enter Closed state.
Enter Locked state.
Enter Unlocked state.
Enter Opened state.
四、小結
在面向對象的軟件系統中,有些對象具有非常復雜的生命周期模型,使用有限狀態機是描述這類對象最好的方法。作為一種軟件設計模式,有限狀態機的概念雖然不算復雜,實現起來也并不困難,但它的問題是當狀態機的模型復雜到一定的程度之后,會帶來實現和維護上的困難。Linux下的FSME是一個可視化的有限狀態機建模工具,而且支持狀態機框架代碼的自動生成,借助它可以更加輕松地構建基于有限狀態機的應用系統。
參考資料
從Wiki百科全書 http://en.wikipedia.org/wiki/Finite_state_automaton開始,你可以了解到許多同狀態機相關的計算理論知識。
狀態機是UML的一個重要組成部分,Robert C. Martin在他的文章UML Tutorial: Finite State Machines中,介紹了如何使用UML語言來對狀態機進行建模,你可以通過網址 http://www.objectmentor.com/resources/articles/umlfsm.pdf可以找到這一文檔。
FSME是Linux下一個基于Qt的狀態機建模工具,它能夠自動生成狀態機框架代碼,并且同時支持C++和Python語言,通過網站 http://fsme.sourceforge.net/你可以了解到有關FSME的更多信息,并能夠下載最新版本的FSME。
Qfsm也是一個運行在Linux下的狀態機建模工具,它不僅提供了可視化的狀態機編輯器,而且還能夠對生成的狀態機進行實時模擬,通過網站 http://qfsm.sourceforge.net/可以了解到Qfsm的更多信息。
免責聲明:本文內容由21ic獲得授權后發布,版權歸原作者所有,本平臺僅提供信息存儲服務。文章僅代表作者個人觀點,不代表本平臺立場,如有問題,請聯系我們,謝謝!
玩轉嵌入式
Linux
代碼
-
在嵌入式軟件開發中,bin、hex、axf和elf這四種格式的文件很常見。 之前我分享的STVP、ST-LINK Utility、STM32CubeProg這些下載編程工具的時候,都用到了bin、hex格式的文章。 作為普通嵌入式軟件開發者,可能只知道如何使用他們,并不會在意這些文件里面具體是什么內容。
1. 總述
bin 和 hex 大家都不陌生,就是我們下載到芯片的程序文件。
bin文件只是單純的程序數據,hex除程序數據之外還有一定格式數據。
而 axf 和 bin、 hex 同樣也屬于程序文件,差別在于 axf 具有更多的調試信息。
用一個表格來區分bin、hex和axf三者的關系:
bin
hex
axf
程序數據
程序數據
程序數據
地址、類型、校驗等標記信息
地址、類型、校驗等標記信息
調試信息
你會發現,同樣一段代碼,編譯生成的bin文件最小,axf最大。
在嵌入式Linux中還有一種文件 ELF(Executable and Linkable Format,可執行與可鏈接格式)也算是一種程序文件,這種文件包含信息更多、更復雜。
下面分別來描述bin、hex、axf和elf這四種格式文件。
2. bin文件
bin 是 binary 的縮寫,直白的翻譯即為二進制文件,在這里理解為可執行的機器代碼(程序)文件,因為計算機存儲只有 0 和 1。
當然,bin 除了是程序文件的含義,還有其他含義,比如虛擬光驅文件,我們下載的一個 Windows 鏡像文件后綴就可能是bin。
bin 相對于hex、axf是一種最簡單的程序文件,只有程序數據,程序文件有多大,程序也就多大。
因此,你下載 bin 程序文件的時候,必須要設置起始地址,比如:通過STM32 ST-LINK Utility工具下載bin文件:
而hex則不可修改(文件中包含地址信息):
3. hex文件
hex 格式文件由 Intel 制定的一種十六進制標準文件格式,是由編譯器轉換而成的一種用于下載到處理器里面的ASCII文本文件。
1.解釋 維基百科解釋 https://en.wikipedia.org/wiki/Intel_HEX Intel HEXThe Intel HEX file is an ASCII text file with lines of text that follow the Intel HEX file format. Each line in an Intel HEX file contains one HEX record. These records are made up of hexadecimal numbers that represent machine language code and/or constant data. Intel HEX files are often used to transfer the program and data that would be stored in a ROM or EPROM. Most EPROM programmers or emulators can use Intel HEX files.
2.格式 hex行格式: :BBAAAATT 【D···D】CC
其中: : 代表行開始,固定為冒號:
BB代表Bytes,數據長度 AAAA代表Address,地址 TT代表Type,數據類型(標識) D···D代表Date,數據
CC代表CheckSum,校驗和
說明:
BB數據長度,也就是D···D這個字段的數據長度;
AAAA地址,起始地址、偏移地址,根據數據類型(TT)有關;
TT數據類型(標識):
00:數據標識
01:文件結束標識
02:擴展段地址
04:線性地址
05:線性開始地址
(地址代表高16位地址,也就是要向左移16bit)
CC校驗和計算公式: CheckSum = 0x100 - (Sum & 0xFF)
3.例子說明 不同數據類型個行數據略有差異,先再看下00(數據類型)的格式:
一個常見hex文件:
:020000040800F2:1000000000040020B1010008FD020008BD02000844:10001000F902000801020008350400080000000091:1000200000000000000000000000000021030008A4···省略數行:100470000000024084040008000000200004000086:040480004804000824:040000050800019955:00000001FF
1.04類型:線性地址行
:020000040800F2
02:數據長度,這里是(0800)地址的2字節長度;
0000:偏移地址,這里數據其實無效;
04:線性地址數據類型;
0800:線性起始地址,左移16位,即:0x0800 0000;
F2:校驗和 F2 = 0x100 - (0x02 + 0x04 + 0x08);
比如,修改起始地址為0600:
2.00類型:數據行
:1000000000040020B1010008FD020008BD02000844
10:數據長度,這里是16字節(程序)數據的長度;
0000:偏移地址,數據第一行偏移0000地址,第二行就是偏移0010,第二行就是偏移0020,依次偏移到FFF0; 如果偏移到FFF0,則會重新下一個起始地址,一段程序你就明白了:
:10FFD000D0C5CFA20D0A00003052010810B50A4862:10FFE00002F0FEFC09A002F0FBFC14A002F0F8FCF9:10FFF0001EA221A123A002F0F3FC2CA002F0F0FC31:020000040801F1:10000000394802F0EDFC10BD3C5301080D0A2A20CE:1000100020202020202020202020202020202020E0:100020002020202020202020202020414756D6C7F5
00:線性地址數據類型;
00040020B1010008FD020008BD020008:程序數據,就是bin文件里面的純程序數據;
44:校驗和 44 = 0x100 - (0x10 + 0x04 + 0x20 + 0xB1 + 0x01 + 0x08 + 0xFD + 0x02 + 0x08 + 0xBD + 0x02 + 0x08 + 0x44) & 0xFF;
3.01類型:文件結束行
:00000001FF
00:數據長度;
0000:偏移地址,這里數據其實無效;
01:代表文件結束;
FF:校驗和
這里代表hex文件結束了,有些公司為了使hex傳輸(下載)更可靠,或通過工具(或命令在)結束行后面追加校驗信息,一般遠程升級會考慮更多校驗信息(后期抽時間講述一下遠程升級)。
更多細節內容,可以參看鏈接:
https://www.keil.com/support/docs/1584/ https://www.kanda.com/blog/microcontrollers/intel-hex-files-explained/ (公號不支持外鏈接,請復制鏈接到瀏覽器打開)
看到這里,我相信很多人都能寫一個腳本工具,讓hex轉為bin文件(后面抽空給大家講述一下hex和bin轉換的工具)。 4. axf文件
axf格式文件是針對ARM編譯器的一種格式文件,它是由 ARM 編譯器產生。
axf文件除了包含程序數據(bin)和地址(hex)等數據之外,還包含調試信息。
axf文件內的調試信息附加在程序文件中,有助于分析和調試。
axf文件的調試信息作用:
可將源代碼包括注釋夾在反匯編代碼中,這樣我們可隨時切換到源代碼中進行調試。
還可以對程序中的函數調用情況進行跟蹤(通過Watch & Call Stack Window查看)。
對變量進行跟蹤(利用Watch & Call Stack Window)。
當然,axf文件調試信息的包含的內容有限,并非所有源碼(及注釋)相關信息都會包含在其中,想要有效調試,還是需要結合源代碼工程進行調試。 5. elf文件
ELF:Executable and Linkable Format,可執行與可鏈接格式。
elf是一種用于二進制文件、可執行文件、目標代碼、共享庫和核心轉儲格式文件。是UNIX系統實驗室(USL)作為應用程序二進制接口(Application Binary Interface,ABI)而開發和發布的,也是Linux的主要可執行文件格式。 ---來源百度百科
elf文件和bin、hex、axf文件同樣屬于可執行文件這一類,但是他們之間差異還是很大,elf文件包含的信息更多,也更復雜。
elf格式文件由四部分組成:
ELF header:ELF頭
Program header table:程序頭表
Section:節
Section header table:節頭表
ELF header:描述整個文件的組織。
Program Header Table: 描述文件中的各種segments,用來告訴系統如何創建進程映像的。
Section:是從運行的角度來描述elf文件,sections是從鏈接的角度來描述elf文件,也就是說,在鏈接階段,我們可以忽略program header table來處理此文件,在運行階段可以忽略section header table來處理此程序(所以很多加固手段刪除了section header table)。從圖中我們也可以看出,segments與sections是包含的關系,一個segment包含若干個section。
Section Header Table: 包含了文件各個segction的屬性信息。
免責聲明:本文內容由21ic獲得授權后發布,版權歸原作者所有,本平臺僅提供信息存儲服務。文章僅代表作者個人觀點,不代表本平臺立場,如有問題,請聯系我們,謝謝!
玩轉嵌入式
嵌入式
軟件開發
-
來源:嵌入式Linux
1. NFC是什么
NFC(Near Field Communication) 技術由Philips、Nokia和Sony主推的一種近距離無線通信技術(NFCIP-1),是一種短距離非接觸式的通信方式,通常有效通訊距離為4厘米以內。工作頻率為13.65 兆赫茲,通信速率為106kbit/秒到 848kbit/秒。通過手機為載體,把非接觸式IC卡應用結合于手機中,以卡、閱讀器、點對點三種應用模式,實現手機支付、行業應用、積分兌換、電子票務、身份識別、防偽、廣告等多種應用的服務產品。
NFC通信總是由個發起者(initiator)和一個接受者(target)組成。
NFC也支持點到一點的通信(peer to peer) 此時參與通信的雙方都有電源支持。
NFC 與其他通信技術對比:
2. RFID是什么
RFID( Radio Frequency Identification),其原理為閱讀器與標簽之間進行非接觸式的數據通信,達到識別目標的目的。RFID 的應用非常廣泛,典型應用有動物晶片、汽車晶片防盜器、門禁管制、停車場管制、生產線自動化、物料管理。
3、NFC和RFID的區別和聯系
NFC和RFID在本質上沒有太多的區別,但是也有一些特點需要區分的,像工作頻率不同,是否支持點對點通信技術。
3.1、 工作頻率
NFC的工作頻率為13.56MHz,而RFID的工作頻率有低頻,高頻(13.56MHz)及超高頻。
3.2. 工作距離
NFC的工作距離理論上為0~20cm,但是在產品的實現上,由于采用了特殊功率抑制技術,使其工作距離只有0~10cm,從而更好地保證業務的安全性。由于RFID具有不同的頻率,其工作距離在幾厘米到幾十米不等。
3.3. 工作模式
NFC同時支持讀寫模式和卡模式。而在RFID中,讀卡器和非接觸卡是獨立的兩個實體,不能切換。
3.4. 點對點通信
NFC支持P2P模式,RFID不支持P2P模式。
3.5. 應用領域
RFID更多的應用在生產,物流,跟蹤和資產管理上,而NFC則工作在門禁,公交卡,手機支付等領域。
3.6. 標準協議
NFC的底層通訊協議兼容高頻RFID的底層通信標準,即兼容ISO14443/ISO15693標準。NFC技術還定義了比較完整的上層協議,如LLCP,NDEF和RTD等。綜上,盡管NFC和RFID技術有區別,但是NFC技術,尤其是底層的通信技術是完全兼容高頻RFID技術的。因此在高頻RFID的應用領域中,同樣可以使用NFC技術。
4、NFC卡片分類
NFC卡片主要分為兩類,ID卡和IC卡,從名字可以知道,IC卡里面有專門處理卡片數據的芯片,ID卡主要用來給NFC讀設備讀取數據。
M1卡: 全稱Mifare classic 1K,普通IC卡,0扇區不可修改,其他扇區可以反復擦寫。通常我們使用的門禁卡、電梯卡都是M1卡。
M1卡是NXP(恩智浦半導體)公司研發的IC卡,執行標準是ISO/IEC14443 Type A,讀寫頻率是13.56MHz。目前大多數手機廠商使用的NFC芯片都是NXP,另一部分則是BRCM(博通)方案,均執行同一標準,這是手機讀寫M1卡的技術基礎。
UID卡: 普通復制卡,可以反復擦寫所有扇區,門禁有防火墻則失效。
CUID: 升級復制卡,可以反復擦寫所有扇區,可以穿透大部分防火墻。
FUID: 高級復制卡,0扇區只能寫入一次,寫入后變為M1卡。
UFUID: 超高級復制卡,0扇區只能寫入一次,封卡后變為M1卡,不封卡變為UID卡。
復制卡均可在網上購買,有普通卡片、鑰匙扣、滴膠卡等類型,CUID通常1.5元/張,越高級的卡越貴。
4.1、M1卡的結構
M1卡標準儲存的數據使用16進制,簡稱HEX,即由0-9、A-F組成,也寫作0xAA
4.1.1 存儲結構
Mifare classic 1K,即存儲容量1K=1024Byte,包括16個扇區,每個扇區含4個塊,每個塊16Byte.
第0扇區比較特殊,0區0塊前8位為廠商UID碼,可以理解為M1卡的識別碼。
0-2塊為儲存內容區間。
3塊為系統保留區間,用于存放卡密碼和控制碼,其中:
3塊前12位為keyA(密碼A),3塊后12位為keyB(密碼B),3塊中間8位為控制碼。
A/B密碼的默認值為12個F或0,翻譯為2進制即4*12個1或0
控制碼默認值為FF078069,意思是A密碼(非默認情況下)不可見,B密碼可見,讀寫驗證A密碼。
4.1.2 讀寫權限
M1卡有4種主要權限:讀、寫、增量、減量。
以及2種附權限:讀寫控制碼、讀寫A/B密碼。
每種權限都要使用A或B密碼、并在控制碼約束下來操作。
基于M1卡的結構及讀取權限特點,M1卡又可以分為非加密、半加密、全加密三種類型。
非加密:16個扇區的A/B密碼均使用默認值。
半加密:0扇區外的某一個或多個數據扇區A/B密碼不是默認值。
全加密:所有扇區A/B密碼不是默認值。(由于M1卡的加密邏輯已經被公開,所以所有的M1加密卡都可以被破解,破解能力PN532
5、防偽系統的原理和破解
常見的防偽系統有三種,
一是加密型,通過對扇區進行加密實現防偽。單純的加密型已經可以通過PN532、PM3等工具完成破解。
雖然卡A/B密碼可以被破解,但真正破解的重頭戲是在于如何找出卡信息的存儲規律,從而進行自定義修改等操作。
二是篡改型,刷卡時系統嘗試寫入0扇區,如果成功,則卡片作廢(CUID特性,0扇區可反復擦寫)。
三是滾碼型,每次刷卡,系統都從特定扇區讀取驗證一段校驗碼,并寫入新的校驗碼。如果不能通過多次刷卡找到校驗碼的規律,則不可復制。
6、復制卡
支持NFC的手機可以模擬白卡,如果把可以使用的卡片數據讀取出來,然后再寫入到手機的卡片中,就可以完成卡片的復制。
比如正常的小區門禁卡,先用一個設備把小區門禁卡里面的數據讀取出來,然后再寫入手機生成的這張白卡中,就完成了卡片的復制。
但是也有問題,手機生成的白卡,并不是一定可以寫入所有扇區的,我的小米手機就不行,0扇區有8個字節會一直保持不變。所以就不可能用我的小米手機來模擬門禁卡,但是天無絕人之路,可以購買那種貼紙大小的NFC卡貼,復制卡片后貼在手機上,同樣可以完成這樣的功能。
免責聲明:本文內容由21ic獲得授權后發布,版權歸原作者所有,本平臺僅提供信息存儲服務。文章僅代表作者個人觀點,不代表本平臺立場,如有問題,請聯系我們,謝謝!
玩轉嵌入式
RFID
NFC
通信技術
-
玩轉嵌入式
數字信號
阻抗
-
程序員最大的悲哀,就是年輕時一心撲在技術上,到頭來卻發現,技術是最不值錢的東西。
為什么這么說呢?看現在的應屆生校招,就會有這樣一個感受:
現在的畢業生越來越優秀,看著他們分享的面經,自己經常覺得慚愧,覺得自己那么多年技術都白學了。
程序員這一行業有門檻,不是誰都可以做的,但是程序員門檻又很低,貌似是個人都能做。
這句話,看似矛盾,其實不矛盾。只要理工科學生,上過大學,或者高中也行,對編程有興趣,自學也好,培訓也好,都能來做編程。
從身邊一些經歷能深刻體會到這一點,現在編程培訓越來越多,進一步降低了程序員的入門門檻,很多人跨專業,跨行業來搞編程,進入計算機行業。
但是這僅僅是個入門門檻,進了門檻,卻不容易提升,想要提升,就要不斷的學習新技術,提高自己的能力,要不就是要有個高學歷,有比較強的科研能力。
像我同學,最近都在忙著筆試,面試,參加校招,面試一個java后端開發,都能卷到系統底層鎖機制,系統內核線程調度問題,各種Hash底層實現,還有各種并發原理。
面試造火箭,工作造輪子的問題似乎成了普遍現象。
好不容易拼進努力進入了大廠,但是35歲退休的說法就像懸在頭上的一把劍。
996的生活已經讓人感到焦慮。加班無休止使得身體越來越吃不消。越來越比不上年輕人。
當你對這種生活感到不滿意,試圖通過自己的代碼或技術來賺錢時,真正的絕望才是開始。
因為你會發現,生活的瑣事和成年人的煩惱,在牽絆著你,你根本沒法像年輕人那樣不管不顧,一心悶頭在技術的鉆研上,你有自己操蛋的生活,有老婆孩子需要照顧,有父母需要贍養,似乎成年人的責任和義務都壓在你頭上。
想著靠接外包項目來賺點外快的話,實際上,當你真的接過幾個之后,就會深有感觸:需求一大堆,客戶又不懂技術,給你提一些天馬行空的想法,需求總是不夠明細,頻繁變化,關鍵還不認同你的技術價值,開的價格高了人直接不干了。異想天開,幾千塊錢就想讓你做個抖音,京東之類的App。
一個人支撐起一個項目實在太難了,需要你會太多東西了,曾經你看不上的美工,UI,設計,甚至運營崗位,這時候你會深刻感受到,很多,并不是技術能解決的事情。
好不容易接個能做的,累死累活加班加點搞差不多,給雇主看的時候,要么各種挑刺,說你的功能沒有做好,要么有些直接跟你玩消失,有些人跟你玩馬虎眼,跟你說一大堆感謝的話,但是卻絕對不談尾款的事情。
很多程序員都有類似這樣的經歷吧??
免責聲明:本文內容由21ic獲得授權后發布,版權歸原作者所有,本平臺僅提供信息存儲服務。文章僅代表作者個人觀點,不代表本平臺立場,如有問題,請聯系我們,謝謝!
玩轉嵌入式
程序員
技術員
-
今天EDA365電子論壇給大家匯總了一些電路抗干擾設計原則,希望能在知識層面給到大家一些幫助。
電源線的設計
地線的設計
3
1) 不要有過長的平行信號線;
2) 保證pcb的時鐘發生器、晶振和cpu的時鐘輸入端盡量靠近,同時遠離其他低頻器件;
3) 元器件應圍繞器件進行配置,盡量減少引線長度;
4) 對pcb板進行分區布局;
5) 考慮pcb板在機箱中的位置和方向;
6) 縮短高頻元器件之間的引線。
去耦電容的配置
5
1) 盡量采用45°折線而不是90°折線(盡量減少高頻信號對外的發射與耦合);
2) 用串聯電阻的方法來降低電路信號邊沿的跳變速率;
3) 石英晶振外殼要接地;
4) 閑置不用的們電路不要懸空;
5) 時鐘垂直于IO線時干擾小;
6) 盡量讓時鐘周圍電動勢趨于零;
7) IO驅動電路盡量靠近pcb的邊緣;
8) 任何信號不要形成回路;
9) 對高頻板,電容的分布電感不能忽略,電感的分布電容也不能忽略;
10) 通常功率線、交流線盡量在和信號線不同的板子上。
6
1)CMOS的未使用引腳要通過電阻接地或電源;
2)用RC電路來吸收繼電器等原件的放電電流;
3)總線上加10k左右上拉電阻有助于抗干擾;
4)采用全譯碼有更好的抗干擾性;
5)元器件不用引腳通過10k電阻接電源;
6)總線盡量短,盡量保持一樣長度;
7)兩層之間的布線盡量垂直;
8)發熱元器件避開敏感元件;
9)正面橫向走線,反面縱向走線,只要空間允許,走線越粗越好(僅限地線和電源線);
10)要有良好的地層線,應當盡量從正面走線,反面用作地層線;
11)保持足夠的距離,如濾波器的輸入輸出、光耦的輸入輸出、交流電源線和弱信號線等;
12)長線加低通濾波器。走線盡量短截,不得已走的長線應當在合理的位置插入C、RC、或LC低通濾波器;
13)除了地線,能用細線的不要用粗線。
7
1)一般寬度不宜小于0.2.mm(8mil);
2)在高密度高精度的pcb上,間距和線寬一般0.3mm(12mil);
3)當銅箔的厚度在50um左右時,導線寬度1——1.5mm(60mil) = 2A;
4)公共地一般80mil,對于有微處理器的應用更要注意。
8
電源線盡量短,走直線,走樹形,不要走環形。
9
首先,EDA365電子論壇提醒大家要考慮PCB尺寸大小。PCB尺寸過大時,印制線條長,阻抗增加,抗噪聲能力下降,成本也增加;過小,則散熱不好,且鄰近線條易受干擾。
在確定PCB尺寸后,再確定特殊元件的位置。根據電路的功能單元,對電路的全部元器件進行布局。
在確定特殊元件的位置時要遵守以下原則:
a. 盡可能縮短高頻元器件之間的連線,設法減少它們的分布參數和相互間的電磁干擾。易受干擾的元器件不能相互挨得太近,輸入和輸出元件應盡量遠離。
b. 某些元器件或導線之間可能有較高的電位差,應加大它們之間的距離,以免放電引出意外短路。帶高電壓的元器件應盡量布置在調試時手不易觸及的地方。
c. 重量超過15g的元器件、應當用支架加以固定,然后焊接。那些又大又重、發熱量多的元器件,不宜裝在印制板上,而應裝在整機的機箱底板上,且應考慮散熱問題。熱敏元件應遠離發熱元件。
d. 對于電位器、可調電感線圈、可變電容器、微動開關等可調元件的布局應考慮整機的結構要求。若是機內調節,應放在印制板上方便于調節的地方;若是機外調節,其位置要與調節旋鈕在機箱面板上的位置相適應。
e.應留出印制扳定位孔及固定支架所占用的位置。
根據電路的功能單元對電路的全部元器件進行布局時,要符合以下原則:
a. 按照電路的流程安排各個功能電路單元的位置,使布局便于信號流通,并使信號盡可能保持一致的方向。
b. 以每個功能電路的元件為中心,圍繞它來進行布局。元器件應均勻、整齊、緊湊地排列在PCB上。盡量減少和縮短各元器件之間的引線和連接。
c. 在高頻下工作的電路,要考慮元器件之間的分布參數。一般電路應盡可能使元器件平行排列。這樣,不但美觀。而且裝焊容易。易于批量生產。
d. 位于電路板邊緣的元器件,離電路板邊緣一般不小于2mm。電路板的形狀為矩形。長寬比為3:2成4:3。電路板面尺寸大于200x150mm時。應考慮電路板所受的機械強度。
10
布線的原則如下:
a. 輸入輸出端用的導線應盡量避免相鄰平行。加線間地線,以免發生反饋藕合。
b. 印制攝導線的寬度主要由導線與絕緣基扳間的粘附強度和流過它們的電流值決定。當銅箔厚度為0.05mm、寬度為 1 —— 15mm 時。通過 2A的電流,溫度不會高于3℃,因此。導線寬度為1.5mm可滿足要求。
對于集成電路,尤其是數字電路,通常選0.02——0.3mm導線寬度。當然,只要允許,還是盡可能用寬線。
尤其是電源線和地線。導線的間距主要由壞情況下的線間絕緣電阻和擊穿電壓決定。對于集成電路,尤其是數字電路,只要工藝允許,可使間距小至5——8mm。
c.印制導線拐彎處一般取圓弧形,而直角或夾角在高頻電路中會影響電氣性能。此外,盡量避免使用大面積銅箔。
否則,長時間受熱時,易發生銅箔膨脹和脫落現象。必須用大面積銅箔時,用柵格狀。這樣有利于排除銅箔與基板間粘合劑受熱產生的揮發性氣體。
11
焊盤中心孔要比器件引線直徑稍大一些。焊盤太大易形成虛焊。焊盤外徑D一般不小于(d+1.2)mm,其中d為引線孔徑。對高密度的數字電路,焊盤直徑可取(d+1.0)mm。
PCB及電路抗干擾措施
電源線設計
電源線的設計
退藕電容配置
來自:EDA365電子論壇
玩轉嵌入式
電路板
PCB
抗干擾設計
-
各主要程序員城市工資變化
# 職能
架構工程師的各地工資:
好吧,工資的差異還是地域造成的,你拖后腿了嗎?
CSDN
作者:有數可據
原文鏈接:https://blog.csdn.net/juwikuang/article/details/104162025
免責聲明:本文內容由21ic獲得授權后發布,版權歸原作者所有,本平臺僅提供信息存儲服務。文章僅代表作者個人觀點,不代表本平臺立場,如有問題,請聯系我們,謝謝!
玩轉嵌入式
程序員