總網頁瀏覽量

2019年2月26日 星期二

【C語言觀念複習筆記】函式指標陣列(Array of function pointer)



之前寫過一篇文章討論過指標陣列的觀念

這個指標陣列的指標當然也是有型態的, 上述文章是char

而也可以是指向function

因為指標陣列在上面文章討論過了, 所以先簡單補充一下函數指標的最基礎觀念 透過下列簡單範例說明:

簡單的範例如下:
#include "stdio.h"

void (*foo)();

void show(void)
{

 puts("OMG\n");

}


int main()
{
 int a;
 foo = show;
 foo();
 
 return 0;
}

執行結果會看到 OMG字串顯示


而函數指標陣列就是結合上面(指標陣列 & 函數指標的觀念)



這邊舉個範例,簡單來說是要將下面這段C code的if-else條件改成以函式指標陣列(array of function pointer)的方式改寫

#include "stdio.h"

#include "stdlib.h"

int plus(int a, int b) { return a + b; }
int minus(int a, int b) { return a - b; }
int multiply(int a, int b) { return a * b; }
int divided(int a, int b) { return a / b; }
int main()
{
 int a, c;
 char b;
 printf("key a Function \nEX: 1 + 1\n");
 scanf("%i %c %i", &a, &b, &c);

 if (b == '+')
  printf("%d %c %d = %d\n", a, b, c, plus(a, c));
 else if (b == '-')
  printf("%d %c %d = %d\n", a, b, c, minus(a, c));
 else if (b == '*')
  printf("%d %c %d = %d\n", a, b, c, multiply(a, c));
 else if (b == '/')
  printf("%d %c %d = %d\n", a, b, c, divided(a, c));
}



原程式碼的寫法的問題在於如果條件越多時,那if-else條件式勢必隨條件加長,易使得程式變得攏長,可透過函式指標陣列的方式改善此問題。
改寫如下
#include"stdio.h"

int plus(int a, int b){ return a+b; }
int minus(int a, int b){ return a-b; }
int multiply(int a, int b){ return a*b; }
int divided(int a, int b){ return a/b; }

int main(){
    int a, c;
    char b;
    scanf("%d %c %d", &a, &b, &c);
    
    int (*cmd[])(int a, int c) = {multiply, plus, NULL, minus, NULL, divided};

    printf("%d\n",cmd[b-42](a,c));  // '*' ASCII = 42(0x2a)  ,'+' ASCII = 43 (0x2b), '-' ASCII = 45, '/' ASCII = 47    

    return 0;
}

按照上述寫法加以測試,例如輸入 6*5 就會看到印出的結果30,執行結果畫面如下:




【若需要嵌入式系統技術輔導課程 可來信洽談合作方式: iws6645@gmail.com,亦可先點擊參考這篇介紹文章



2019年2月15日 星期五

許多人對於8051 MCU常有的錯誤觀念討論與闢除


【若需要阿良的嵌入式系統技術輔導課程 可來信洽談合作方式: iws6645@gmail.com,亦可先點擊參考這篇介紹文章

歡迎透過合法的方式分享此文內容
,若要轉載/轉貼,請明確貼出此原始連結並標示作者基本資訊請勿抄襲及非法轉貼(例如擷取內文但並未註明出處)

[8051 MCU好教材分享 & 錯誤觀念闢除]

對於想學MCU(單晶片微控制器)韌體、嵌入式系統硬體(嵌入式處理器架構、外部電源電路、感測器/馬達介面電路、周邊驅動)實作基礎但又會沒有辦法確定學習平台與作法的朋友,可以參考這篇文章對於一些常見的錯誤認知觀念的討論與說明

: 我個人在過去的第一份業界工作其實是在大型SoC上面跑OS的系統(Embedded Linux/安卓等)上面的軟韌體工作,相較於MCU系統比起來,是較偏向嵌入式系統的軟體一些。但站在長年接觸MCU的一位工程人員,並由於自身是電機電子和資工科系都讀過也接觸過的人(從類比電路走到純軟體),一路上看到很多人因各自背景導致相互不理解、文人相輕等狀況,所以還是有些觀點想分享一下,之前也有寫過一篇觀念文章: Arduino到底是什麼? MCU又是什麼?

常見的錯誤觀念及對應說明如下:

(1) 以為MCU位元數少(比不上現代許多高大上的SoCCPU)就代表很簡單
會寫APP或接觸過Embedded Linux系統不代表就懂MCU的應用設計,更不代表設計MCU應用系統很簡單。事實上,就跟多數的技術或學問一樣,MCU類的系統的設計有淺有深(我想大多數技術也是如此),當然如果是single thread(while(1) infinite loop)的狀況下,就純軟體的角度來看,MCU的韌體在軟體方面的架構複雜度確實不高。但除此之外MCU應用系統設計還包含除了徹底理解MCU架構、整合設計電子電路(可能有數位、有類比)、理解各種電子元件的應用特性(即便不是身為純硬體工程師,但很可能為了因應系統整合的需求,也要去了解硬體特性相關方面的技術和知識)Sensor的訊號演算處理(DSP方法設計),甚至是設計控制系統的能力(視產品而定)

這類的系統設計所需要的技術背景知識,相當的視產品別及產品應用場域而定(軟硬體設計經驗和理解程度對於系統好壞的影響甚大)簡單來說,如果只會寫純軟體而不具備上述硬體知識與軟硬體整合能力的人員,是完全沒有能力去做好MCU系統設計的,因為這種韌體的用途重點就是在於驅動/控制硬體電路,當然要懂硬體才能做好這類系統的設計。過去還在校或者在公司時,就看過不少純軟體背景的人不甚理解此點,他們以為MCU的位元數少、規格相較於PC或網路多媒體類的SoC來說簡單很多,就代表著該技術領域很簡單,客觀來說這真的是嚴重錯誤的認知。

另外,嵌入式系統大致可粗分兩種: 硬體控制或大量資料處理,前者常用於控制或不需要本地進行大量運算的場域(如馬達、穿戴式裝置的裝置部分),後者常被用在網路多媒體應用(如數位電視、智慧型手機、Camera)MCU本來就是被用於前者較多(所以其處理器的運算能力規格通常不如後者複雜,這是需求選擇考量,而非是技術高低問題),當然近年來MCU也有被用於多媒體應用的情況出現,有些情況及產品需求下會在MCU上跑小型即時作業系統RTOS(要求即時性但又需要多工排程時),介於上述兩種狀況之間,但比例上仍然較少些

(2) 會簡單應用Arduino就是會MCU
這是學校學生甚至許多教師常犯的觀念錯誤,事實上Arduino API因為已經將底層韌體都包裝成相當方便的API,單純會應用距離具備MCU系統產品設計的能力事實上是有一段很大落差的。事實上,Arduino是很好的平台,可以節省很多不必要的自己刻底層driver(省略掉周邊IP的原理的理解/register setting flow和晶片外部的介面電路及元件的整合原理)的時間,熟悉MCU原理及系統設計的人去用Arduino其實可以用的很好,增加工作效率。反之,不懂原理的人去使用Arduino變成只是一知半解(甚至不到一知半解的程度)。雖然同樣都是使用Arduino,但上述這兩者之間是有很大的差異的。

(3) 805130幾年前的老晶片,就以為它過時且無用了
我甚至在過去看到某OS高手透過暗示的方式對採用8051作為教材的課程進行批評過(他覺得當時的交大和成大某些系所還在教8051就是太過於老舊落伍,我想這大概是領域雖有重複但仍差異很大,所造成的認知落差)

事實上,8051仍然是很適合初學者學習MCU的一個好選擇,因為許多的基礎原理是相通的,是一路發展上來的,現代的MCU並非是突然一蹴可及的。當初學者深入理解8051的原理後,要再去學一些更新的近代MCU其實並不困難,反而容易在初學期間打好基礎觀念,因為8051是相對單純一些的MCU(因為是比較早期出來的),而且容易因為8051這種具體而微的MCU架構(相較於現代許多已經複雜化的32bits MCU而言),容易增加初學者的學習信心

而且其實我看過很多學生(包含許多頂大的大學部學生,甚至是碩士與博士畢業生),光是8051這種較簡單的MCU架構原理(不僅是針對8051這款MCU的觀念,而是指MCU的一些共通觀念),大多數人都沒有學得很透徹(事實上大多數的人根本不熟)。其原因很多,例如只有一學期時間過短、教師認知錯誤、或者只是應用而沒機會深入思考接觸、與教授在論文題目的要求而產生的落差等等。甚至是一些在業界上班的工程師也是如此,也有很多人只是用到,東西會動就以為自己會了,事實上理解程度仍有深淺之分,此點與上述(2)以為會call Arduino API就是代表會設計MCU系統了的這種誤解,是相同道理。


另外,因為產品開發生態系的緣故,8051(或其增強型)的MCU,仍然相當活耀於產業界(如TI德州儀器公司的WSN SoC CC2530或BLE SoC CC2540這些較小型且無MMU的SoC就是based on 8051 core,如下方圖片所示,圖片皆為TI的datasheet)






上述幾間IC設計公司也都還有8051相關MCU IC產品,如上面這些網頁連結所示


另外,因為發現還是有不少朋友無法完整理解上述文章內容,在這裡做一些常見的錯誤觀念迷思的Q&A給大家參考,如下面這篇文章(請點連結進入)


為了貼心服務懶得點連結的朋友,這邊也將內容貼出來如下

Q1: 32bits ARM based的MCU不是比較好嗎?何必學這麼老舊的8051 MCU?

A1: 雖然上面內容中已經有說明,但這裡再詳細解釋。

很多人都誤以為用規格比較強的學習教材(嵌入式硬體平台),就代表自己神功附體,以為這樣子就可以讓學習效率大增。事實上,許多人常迷信於表面,以為自己用所謂ARM based的平台就代表自己比較厲害? 但其實在MCU應用開發的這塊,有多少人有去了解ARM CPU架構和指令集?多數人(包含許多在坊間補習班開課的講師們)通常也只不過將這個平台當成一塊MCU去跑code去設定周邊的registers而已不是嗎?甚至因為這些設定太冗雜繁複,所以很常都只是call高度現成的API,這樣會比以組合語言來學習8051底層基礎還好嗎?而且有真正摸到ARM CPU架構嗎? 終究不就只是在玩一塊比較複雜的MCU? 真的有因為用ARM based的MCU所以就有去實際接觸到ARM CPU嗎? 終究不也是只是一樣在應用MCU內的各式周邊(如UART、I2C、SPI等等)? 其實CPU這塊當然是要有人理解的,例如搞出這個MCU軟體編譯工具與開發環境的工程師們,但這塊大多數已經被開源社群與IC design house做掉了,對於MCU應用開發者來說,這塊已經是現成的工具。舉個例子,多數人玩MCU的C語言程式起跑點是main function,但是事實上在code flow跑到main function之前早就已經必須先去做其他事(startup),例如針對堆疊進行初始化等等(否則哪有C語言裡面這樣可以呼叫到函數跳來跳去的流程可用呢?),這塊是必要的,但是多數MCU應用者是拿現成的。好,把話題拉回來,用32bits MCU給初學者學,初學者真的會去細細探究這些已經是現成但較深入的部分嗎? 不會的,反而會因為32bits MCU的周邊大多較新穎又比8051的周邊複雜許多,而會去忽略一些重要的周邊操作員裡細節基礎,而且也會因此而不敢挑戰這裡所說的所謂現成的部分(如透過Assembly組合語言去熟悉指令集),畢竟ARM(即便是ARM Cortex-Mx系列)指令集對於多數的所謂MCU應用初學者來說太複雜了(當然做MCU系統應用開發也未必就一定要很懂CPU這部分,但是用簡單具體而微的平台來熟悉一次這些細節,其基礎不是會更紮實嗎? 而且也會包含一些重要的細節如中斷、堆疊等底層原理,這就是我說的為何MCU初學者應該要先以8051這類較簡單MCU來入門的原因。並不是拿8051來應用,而是透過較簡易的MCU來徹底的了解一次MCU底層的這些細節,在經歷過這樣的過程後,未來要再接觸較複雜的MCU基本上也不是太大的問題)。反過來,如果一開始就學32bits MCU,不但可能沒有真正學到什麼ARM CPU架構,而且反而可能會因為這類MCU周邊的複雜度而混淆初學者應該要學到的一些重點,反而未必會讓基礎紮實。例如某些32bits MCU的Timer模式眾多且其對應設定過程相對繁複,因為這是針對許多現代各式產品應用而生,但這對於多數初學者而言可能太複雜了,其實初學者只要從8051那類較簡易的Timer去理解什麼是Timer和實際上如何使用的原理就夠了,而往後如果需要了解更複雜的Timer相信也不會是太大的問題,寧可不要讓初學者因為太複雜的周邊導致觀念混淆或者提前放棄。

其實接觸國內名校的這些年來,我發現即便光是8位元的MCU,其實我沒看過有多少名校學生真正在第一次課程內就學得好的,至今依舊如此(MCU基礎範圍內其實還有基本介面電子電路等硬體觀念,並非只有MCU本身)。如果一開始就用複雜的MCU當教材,只是表面上好像用很新很強的平台在學習,但實際上並沒有讓初學者學得比較紮實,拿樹莓派為例,樹莓派其實是個非常非常龐大的系統,許多初學者或學生表面上都會用,但是這類平台下面的內容硬要說起來就多得跟大海一樣廣又深,表面上的功能會動並不代表學生已經掌握了這些內容,只是拿了很多現成的東西去做相當後續的應用而已...我的第一份工作的公司就是做這類平台的,從這種大SoC IC到上面的整個系統軟韌體,這個複雜度比MCU還高太多太多(所以這種公司的研發工程師人數也很多),而許多初學者或學生甚至規劃課程的老師們並沒有去了解以及看到這個面向。而自己曾經帶過的同學們也蠻多在國內知名的MCU IC設計公司服務,所以整體來說,從產業經驗來回頭看,我還是覺得8051蠻適合教學的。

--------

Q2: 8051時脈這麼慢,學這有用嗎?

A2: 其實這又是類似Q1的問題(千篇一律),我認為對於專業技術的學習效率應該不是硬體規格問題,學習一門技術所應該著重的是基礎,而不是在比較實驗教材的規格強弱(就像是使用個人電腦的人就代表懂個人電腦這麼複雜的系統設計嗎),何況MCU在多數的應用上通常本來就不是在比速度的呀(這是用途差異的問題),例如做許多I/O控制應用大多不需要太過於高速,畢竟需要等待例如馬達這些設備的反應,這些機電設備的反應動作速度是遠比不上MCU內的CPU的。如果在應用上需要複雜的演算,從系統設計的角度上來說,也未必適合在MCU端來做全部的演算,應由本身具備較強運算功能的平台專門做演算。(補充一下,近年來ST等大廠有一些很不像傳統MCU的較高速的MCU產品推出,通常用在某些具備多媒體應用功能的消費性電子產品裝置,但這類的MCU平台規格已經和網路多媒體類的SoC或Embedded Microprocessor這類通常具有MMU的CPU的平台的應用很相近了)。

--------

Q3: 近代新的MCU大多是32 bits,很多還是based on ARM Cortex-Mx CPU的MCU(如STM32/笙泉/盛群/新唐/松翰等廠商接有出產這類的MCU IC產品),若用8051會不會因為期開發環境太老舊而導致學習者在往後無法適應32 bits MCU呢?

A3: 這不太是問題,因為現代MCU應用開發,不論8051 MCU或者32bits MCU,其開發環境基本上都是使用現成的圖形介面IDE(整合開發環境,如KEILC、IAR、STM32Cube等等),其環境大同小異(應該說幾乎一樣)。真正與MCU開發環境差異大的是Embedded Linux或者更精確地說是Linux kernel space相關系統軟體的開發,幾乎全部都是透過terminal敲指令式的開發環境,這種環境下很可能根本沒有IDE這類現成的圖形化介面可用,且就開發文化/生態系而言大多還是指令式較多

--------

Q4: 繼Q1,初學者或學生們會因為用ARM based的MCU或SoC板子而突然神功附體、學習效益突飛猛進嗎?

A4: 不會。因為初學者和學生的普遍學習狀況並不會因為硬體平台規格增強,所以就學得比較好和比較多。實際上,對於大多數的初學者來說,硬體平台複雜反而容易忽略很多重要的觀念,正因為複雜,為了能看到東西會動(功能會動),反而會使用表面或現成的內容草草帶過,其觀念容易零碎化。與其如此,那還不如用麻雀雖小五臟俱全、具體而微、易於學習掌握的平台,還比較能夠掌握到底層的細節基礎,雖然是具體而微的平台,但在學習體會過這些基礎內容之後,未來再去接觸複雜的平台也比較能接受,也比較有觀念,比較不會太過零碎

--------

Q5: 那照上面這樣說,初學者學習MCU是否一定只能學8051不可?

A5: 當然不一定只能用8051不可! 我也不是要當什麼8051的死忠擁護愛戴者,這些東西(平台)都是死的,沒什麼好迷信或死忠的。關鍵是要能能從這些東西(平台)學到紮實且能延伸融會貫通的技術經驗和專業知識,才是核心重點。
如果今天有一款平台像是8051這樣,擁有麻雀雖小五臟俱全且具體而微的特性,複雜度不至於太高(對初學者來說其實如果談細節的話還是不容易)的MCU,而且又有很多技術資料可參考(容易取得),那當然絕對不一定只能用8051。
會推薦8051的原因只不過是因為這是一顆快要40年的長青樹等級的MCU,除了擁有較容易入門的特性外(事實上如果是用assembly並且針對MCU硬體架構細節原理去琢磨也已經有一些複雜度了),其技術資料和各路技術高手與網友經驗非常多,初學者相當容易取得這些資料來做為學習教材,所以才會這麼推薦用8051 MCU來作為教學平台。
另外再次重申/強調,教學應該著重於基礎技術觀念的紮實度,而不該只是看重讓東西(功能)會動就好,既然應該著重的是基礎的紮實程度(如堆疊、中斷、常見周邊的運作基本原理特性等),所以用8051這種在此些方面複雜度不算太高的平台是再適合不過了

【若需要阿良的嵌入式系統技術輔導課程 可來信洽談合作方式: iws6645@gmail.com,亦可先點擊參考這篇介紹文章

2019年2月12日 星期二

[python] 當遇到ValueError: need more than 1 value to unpack錯誤訊息


當撰寫python code時

如果透過split('符號')method去讀取某個文字檔的時後,遇到下列直譯錯誤訊息:

ValueError: need more than 1 value to unpack

通常就是讀取的文字檔可能有某行沒有你split method內所指定的符號(有時可能只是不小心按下enter產生的空白行,因為這種空白行通常不會有任何你指定的符號所以會錯誤)

如果是上述不小心按下enter產生的空白行可以刪掉該空白行就好,或者透過find method搭配條件式偵測該行有無指定符號,若無則透過continue跳過該次處理該行的迴圈,直接進行下一次迴圈的執行

關於這部分,網路上有篇文章說明得很清楚(有以實際範例來說明),可參考: https://www.jianshu.com/p/c0e43f918920

該篇文章的範例文字檔內容: http://python.itcarlow.ie/chapter3/sketch.txt 

【若需要嵌入式系統技術輔導課程 可來信洽談合作方式: iws6645@gmail.com,亦可先點擊參考這篇介紹文章