2019年6月12日 星期三

FPGA Verilog 的學習經驗,提供給要入門的新手

今天簡單說說 FPGA Verilog 的學習經驗,提供給要入門的新手:
1.對自己寫的FPGA Verilog程式,所生成的數位電路要心中有數。 這一點個人認為很重要,就正如寫 C語言,心中要能生成對應的組合語言一樣,我是這樣要求自己的。
雖然 FPGA Verilog語言很像 C語言,但Verilog和C語言在本質上是不同的,因為Verilog進行的是數位硬體設計,寫出來的程式就是實實在在數位電路。所以最重要的就是"對自己寫的FPGA Verilog程式,所生成的數位電路要心中有數"。

2. FPGA Verilog所實現的數位電路,重要的一點觀念是:同步原則,這可由其Verilog語法所生成的RTL Viewer查看(以Quartus為例)即知。然在設計時在異步電路會有不可控性,很可能一不小心可能有毛刺產生,而在整個FPGA內部的任何一點毛刺,都會一級一級的傳遞下去,最終影響系統的穩定性。
正因如此,我在大部分的設計均是採同步電路方式。
而在要採用哪種開發工具,我的觀點是:工具永遠就是工具,最重要的是學會怎麼使用它,不用去太細究研究它,用的多了自然就熟練了。

我在教新人如何入門FPGA Verilog時,時常強調"對自己寫的FPGA Verilog程式,所生成的數位電路要心中有數",會大量使用FPGA Verilog程式與生成的RTL Viewer查看比對。
現我分享, 一個由網路上擷取下來的 moore狀態機的Verilog程式,稍加改寫,並寫一支 Test Bench程式,執行ModelSim產生波形,擷取下來。且也生成 RTL Viewer 查看比對。

有關 FPGA State Machine的寫法與討論,網上很多請自行搜尋查閱。在State Machine我個人常用moore狀態機。
簡單說說moore跟mealy狀態機,無論是mealy還是moore,下一個狀態改變都依賴於input跟目前狀態。兩者不同之處在於其輸出:在mealy,輸出取決於當前輸入和狀態。在moore的輸出,僅是一狀態函數。兩者的優點和缺點:mealy會更快。從某種意義上說,輸入轉換會在輸入轉換符合邏輯時立即改變,這樣mealy是很快反映,但mealy是異步的。
在moore,它必須等待一個時鐘週期,因為moore隨著狀態而變化,moore是同步的。
兩者缺點:moore的輸出比mealy會慢一個 Clock。 而 mealy 最重要的缺點是可能會導致Metastability(動態穩定性),因而可能有 "毛刺" 產生,以導致影響系統的穩定性。

現分享,為讓自己完全清楚: 自己寫的FPGA Verilog程式[如圖1],所生成的數位電路要心中有數[如圖2]。 以及明明白白清楚會產生如何的波形[如圖3]。



2019年2月2日 星期六

使用Arduino完成 RFID 結合Keypad鍵,可控制門鎖(或開關)之應用系統

使用Arduino完成 RFID 結合Keypad鍵,可控制門鎖(或開關)之應用系統
 
此範例參考自 Arduino 官網的RFID範例並改寫[註]。官網的周邊與我的有差異,所以需自行修改程式碼以符合我手上的硬體周邊。
 
此範例硬體系統有:
1. Arduino Uno(也可以是任何CPU)
2. MFRC522 RFID Reader 與 RFID tag
3. 4X4 Keypad
4. led *3, 左邊 led 在偵測到合法 RFID tag 亮起、 中間led 在提示輸入密碼後,正確輸入 3939(正確的密碼)後 亮起、右邊led 是輸入錯誤的密碼亮起; 當偵測到不合法 RFID tag時,亮 中間與右邊的led燈。
 
合法RFID UID是: "3D AB BD B2"
密碼: 3939
 
影片網址:

我原本要找一支應用範例,以 RTOS(即時作業系統)方式改寫,後來觀察此應用情境,看起來應該不需用到RTOS,所以就簡單改完後分享。
 
FB: https://www.facebook.com/groups/233019834173788/permalink/389822991826804/

[註]:
1. Raspberry 樹梅派 與 Arduino 在網上有非常多的 應用範例,參考後稍微改改,可以讓初學者快速了解。
2. 而在Raspberry 樹梅派是嵌入式領域,最好能學到也可以在任何不同的 ARM SoC(例如 Freescale/NXP 的i.MX任意CPU),寫上一支Linux AP透過Linux驅動程式控制硬體。
3. 而在Arduino,可以隨意接上任意周邊,看著周邊硬體的Spec,寫出正確的驅動程式

2018年12月31日 星期一

Qt 5 Signal/Slot連接方法技術細節分享


Qt 5 Signal/Slot 連接方法技術細節分享:
Qt 5.0 提供了兩種編寫Signal/Slot連接的方法:基於字串的連接語法(需搭配 moc + SIGNAL/SLOT )基於functor-based的連接語法(使用PointerToMemberFunction)
先列出 Qt 4以前的Signal/Slot連接的語法(基於字串的連接語法,Qt4.8版本):
connect(const QObject *, const char *, const QObject *, const char *, Qt::ConnectionType) : bool
--> 完整語法 bool QObject::connect(const QObject * sender, const char * signal, const QObject * receiver, const char * method, Qt::ConnectionType type = Qt::AutoConnection)
connect(const QObject *, const QMetaMethod &, const QObject *, const QMetaMethod &, Qt::ConnectionType) : bool
connect(const QObject *, const char *, const char *, Qt::ConnectionType) const : bool

disconnect(const QObject *, const char *, const QObject *, const char *) : bool
disconnect(const QObject *, const QMetaMethod &, const QObject *, const QMetaMethod &) : bool
disconnect(const char *, const QObject *, const char *) : bool
disconnect(const QObject *, const char *) : bool

以下是 Qt 5目前的Signal/Slot連接的語法, connect 多三個,加了PointerToMemberFunction Functor:
connect(const QObject *, const char *, const QObject *, const char *, Qt::ConnectionType ) : QMetaObject::Connection
connect(const QObject *, const QMetaMethod &, const QObject *, const QMetaMethod &, Qt::ConnectionType ) : QMetaObject::Connection
connect(const QObject *, const char *, const char *, Qt::ConnectionType ) const : QMetaObject::Connection
connect(const QObject *, PointerToMemberFunction , const QObject *, PointerToMemberFunction , Qt::ConnectionType ) : QMetaObject::Connection
connect(const QObject *, PointerToMemberFunction , Functor ) : QMetaObject::Connection
connect(const QObject *, PointerToMemberFunction , const QObject *, Functor , Qt::ConnectionType ) : QMetaObject::Connection

disconnect(const QObject *, const char *, const QObject *, const char *) : bool
disconnect(const QObject *, const QMetaMethod &, const QObject *, const QMetaMethod &) : bool
disconnect(const char *, const QObject *, const char *) const : bool
disconnect(const QObject *, const char *) const : bool
disconnect(const QMetaObject::Connection &) : bool
disconnect(const QObject *, PointerToMemberFunction , const QObject *, PointerToMemberFunction ) : bool

Qt 5繼續支持舊的基於字串的連接語法, 因這兩種連接語法各有優缺點, 簡述如下:

基於字串的連接語法
基於functor-based的連接語法
Type checking在何時完成
Run-time
編譯時
可以執行隱式類型轉換

Y
可以將信號連接到lambda表達式

Y
可以將信號連接到參數多於信號的插槽
Y

可以將C ++函數連接到QML函數
Y




2018年8月16日 星期四

深度探索Arduino[In-depth exploration Arduino] 之 2 :在Arduino上執行 FreeRTOS創建兩個Task(TaskBlink1,TaskBlink2),加入外部中斷控制,利用Binary semaphores實現中斷與TASK同步

深度探索Arduino[In-depth exploration Arduino] 之 2 :在Arduino上執行 FreeRTOS創建兩個Task(TaskBlink1,TaskBlink2),加入外部中斷控制,利用Binary semaphores實現中斷與 TaskBlink2同步,兩個Task分別控制不同的LED。

範例DEMO影片:

功能描述:
TaskBlink1 執行 void TaskBlink1(void *pvParameters) 函數,其優先權是1(數值越低優先權越低,0是最低優先權),TaskBlink1僅在固定時間將LED做toggle(閃爍切換)。

TaskBlink2 執行 void TaskBlink2(void *pvParameters) 函數,其優先權是3 高優先權。而TaskBlink2必須成功“獲取” semaphore時,才往下執行程式,所以 TaskBlink2 嘗試“獲取”semaphore時,當無法獲取時會進入阻塞狀態(Blocked state),且這裡程式設定阻塞時間最大的 'ticks' 數是portMAX_DELAY,所以 TaskBlink2 會被阻塞(Blocked)在此,等待“獲取”semaphore。程式片段如下:
if( xSemaphoreTake( xSemaphore, portMAX_DELAY ) == pdTRUE )

此 FreeRTOS/Arduino程式範例加入外部中斷,設定buttonPin 2有按下時,在下降緣觸發 ExternalInterrupt 中斷Handler,此 ExternalInterrupt 中斷Handler是送出一semaphore,來實現 ExternalInterrupt中斷與 TaskBlink2同步。程式片段如下:
void setup() {
  ...
  pinMode(buttonPin, INPUT);
  attachInterrupt(digitalPinToInterrupt( buttonPin ), ExternalInterrupt, FALLING);
  ...
}
static void ExternalInterrupt()
{
  static BaseType_t xHigherPriorityTaskWoken = pdFALSE;
  xSemaphoreGiveFromISR( xSemaphore, &xHigherPriorityTaskWoken );
  if( xHigherPriorityTaskWoken == pdTRUE )
    vPortYield();
}

當TaskBlink2成功獲取semaphore時,就往下執行,讓另一個LED亮一下,程式片段如下:
void TaskBlink2(void *pvParameters)  // This is a task.
{
  (void) pvParameters;
  pinMode(12, OUTPUT); // initialize digital LED on pin 12 as an output.
  digitalWrite(12, HIGH);   // turn the LED on (HIGH is the voltage level)
  for (;;)
  {
    if( xSemaphoreTake( xSemaphore, portMAX_DELAY ) == pdTRUE )
    {
      digitalWrite(12, LOW);    // turn the LED off by making the voltage LOW
      vTaskDelay( 500 / portTICK_PERIOD_MS ); // wait for one second
      digitalWrite(12, HIGH);   // turn the LED on (HIGH is the voltage level)
    }
  }
}

最後一個很重要的觀念,簡單介紹,FreeRTOS的Binary semaphores與Mutual exclusion兩者功能很相似,但有一些細微差別。Mutual exclusion包括優先級繼承機制,而Binary semaphores是不包含。所以使得Binary semaphores成為實現像任務之間或任務與中斷之間的同步是更好的選擇,那Mutual exclusion是實現簡單互斥的最佳選擇。

完整程式碼:

2018年8月8日 星期三

使用VMware Workstation 12 Player開啟 Linux OS虛擬機:以Ubuntu 14.04 LTS為例

使用VMware Workstation 12 Player開啟 Linux OS虛擬機:以Ubuntu 14.04 LTS為例

1. 免費虛擬機器VMware Workstation 12 Player下載與安裝,請參考:
http://blog.xuite.net/yh96301/blog/63289807 網址步驟 1 到 17.點選「Finish」。

2. 請先下載已經在 VMware Workstation Player上安裝好的Linux OS作業系統壓縮檔,下載後請先解壓縮在夠大的硬碟空間。
[註]: 下載網址會事先EMAIL通知, 建議安裝VMware Workstation 12 Player,當然也可以選擇安裝 VMware Workstation 13 Player以後的版本

3. 以Administrator權限執行VMware Workstation Player。

4. 在VMware Workstation Player開啟的視窗如圖示,點選「Open a Virtual Machine」,可以在虛擬機器執行預先安裝好的Linux OS作業系統。

5. 至step 2所解壓的硬碟目錄,開啟對應的 vmx 檔。


5.1 點選 Edit virtual machine settings,設定適當的執行參數


5.2 於Virtual Machine Setting的 hardware 頁籤,依據你的電腦之CPU核心數與RAM大小設定適當的參數值:

[註]:此處是設定 Memory 8GB, Processor 是4

6. 之後點選 Play virtual machine,執行此 Linux Kernel Image。

VMware Workstation Player成功執行預先安裝好的Linux OS作業系統,畫面:
7. 至此已成功在VMware Workstation Player執行預先安裝好的Linux OS作業系統。

8. 結束VMware Workstation Player上執行的Linux OS作業系統, 記得正確關機,如下圖所示。


之後點選右邊的 [Shut Down] 。

注意事項:
1.有可能執行錯誤狀況: 在VMware Player中啟用UEFI模式
2.確定自己的CPU是否為64位元之外,也要檢查在開機時登入的BIOS裡,Intel VT-x是否有啟動
3.查詢CPU是否有支援Intel VT-x → Intel or AMD
PS: 儘可能安裝 VMware Workstation 12 Player版本(還是VMware Workstation 14 Player??)
4.電腦或筆電的CPU不要太弱(如賽揚Celeron的就不行),硬碟空間須有12G以上,RAM記憶體要8G以上

解決參考網址:
1. https://hk.saowen.com/a/d7a36aaba964cccc4bda293c1f720c51e1b59350364887bd6493fd95be82c339
2. http://ktess1020.pixnet.net/blog/post/286039357-vmware-player-%E5%AE%89%E8%A3%9D64%E4%BD%8D%E5%85%83%E7%B3%BB%E7%B5%B1
3. http://keikoblog.blogspot.tw/2011/04/vmware-64-bit-guest-os-support.html

2018年8月2日 星期四

Python 源碼解析之 list 與 tuple 的 特性與 操作methods 比較

本文最終將清楚了解:
得知了 list 與 tuple 的 methods 之差異,例如 list 可以 sort 排序, 而 tuple 就不能 sort 排序;

以及兩者的特性:
List其特性有:
List is a collection which is ordered and changeable. Allows duplicate members.
且tuple因其特性有:
Tuple is a collection which is ordered and unchangeable. Allows duplicate members.
是ordered順序性與unchangeable(不可改變)的特性。

內文開始:
使用Python語法開發的工程師,應該都知道list 與 tuple這兩種的差異性。也應熟知它們之間可用的methods,list是比較多,tuple是比較少。
List其特性有:
List is a collection which is ordered and changeable. Allows duplicate members.
且tuple因其特性有:
Tuple is a collection which is ordered and unchangeable. Allows duplicate members.
是ordered順序性與unchangeable(不可改變)的特性。為何呢??

當你是資深Python工程師,我個人觀點是要很深入了解,Python其底層源碼是什麼? 才會讓 List 與 tuple 兩種有些差異性。 [ 註: 或者是從事 Python 程式設計講師 也是要非常熟源碼 ]

先剖析 List 是 ordered and changeable;tuple是 ordered and unchangeable。

觀察Python 源碼的 listobject.h 與 tupleobject.h,

在 listobject.h 有 PyListObject 的宣告:
typedef struct {
    PyObject_VAR_HEAD
    PyObject **ob_item;
    Py_ssize_t allocated;
} PyListObject;

在 tupleobject.h有 PyTupleObject 的宣告:
typedef struct {
    PyObject_VAR_HEAD
    PyObject *ob_item[1];
} PyTupleObject;

兩者第一個 struct member是 PyObject_VAR_HEAD,這定義在 object.h,有關之程式碼如下:

#define PyObject_VAR_HEAD PyVarObject ob_base;

PyObject_VAR_HEAD是個巨集,其marco defination是 PyVarObject ob_base;

再來 PyVarObject 的定義如下:

typedef struct {
    PyObject ob_base;
    Py_ssize_t ob_size; /* Number of items in variable part */
} PyVarObject;

PyVarObject第一個member是PyObject,其定義如下:

typedef struct _object {
    _PyObject_HEAD_EXTRA
    Py_ssize_t ob_refcnt;
    struct _typeobject *ob_type;
} PyObject;

PyObject第一個member是 _PyObject_HEAD_EXTRA ,其定義如下:

再分析一下源碼的 _PyObject_HEAD_EXTRA,

#ifdef Py_TRACE_REFS
/* Define pointers to support a doubly-linked list of all live heap objects. */
#define _PyObject_HEAD_EXTRA \
    struct _object *_ob_next; \
    struct _object *_ob_prev;
#define _PyObject_EXTRA_INIT 0, 0,
#else
#define _PyObject_HEAD_EXTRA
#define _PyObject_EXTRA_INIT
#endif

這段是如果定義了 Py_TRACE_REFS 則 Objects 會變成是一個雙向列表, 但是在release版本下沒有定義 Py_TRACE_REFS , 因此這個 MARCO 可以忽略.

最終的 list 與 tuple 之結構型態的 member 呈現分別是:

在 listobject.h 有 PyListObject 的宣告:
typedef struct {
    Py_ssize_t ob_refcnt;
    struct _typeobject *ob_type;
    Py_ssize_t ob_size;
    PyObject **ob_item;  //此處PyObject宣告了List的 ob_item
    Py_ssize_t allocated;
} PyListObject;

在 tupleobject.h有 PyTupleObject 的宣告:
typedef struct {
    Py_ssize_t ob_refcnt;
    struct _typeobject *ob_type;
    Py_ssize_t ob_size;
    PyObject *ob_item[1]; //此處PyObject宣告了 tuple 的 ob_item
} PyTupleObject;

在最終的 list 與 tuple 之結構型態知道,list 是以 PyObject **ob_item; 來處理其 item data;
而 tuple 是以 PyObject *ob_item[1]; 來處理其 item data。
因其型態特性不同,致在其對應的PyTypeObject也不同。

list 的 PyTypeObject 源碼在 listobject.c,程式如下:
PyTypeObject PyList_Type = {
    PyVarObject_HEAD_INIT(&PyType_Type, 0)
    "list",
    sizeof(PyListObject),
    0,
    (destructor)list_dealloc,                   /* tp_dealloc */
    0,                                          /* tp_print */
    0,                                          /* tp_getattr */
    0,                                          /* tp_setattr */
    0,                                          /* tp_reserved */
    (reprfunc)list_repr,                        /* tp_repr */
    0,                                          /* tp_as_number */
    &list_as_sequence,                          /* tp_as_sequence */
    &list_as_mapping,                           /* tp_as_mapping */
    PyObject_HashNotImplemented,                /* tp_hash */
    0,                                          /* tp_call */
    0,                                          /* tp_str */
    PyObject_GenericGetAttr,                    /* tp_getattro */
    0,                                          /* tp_setattro */
    0,                                          /* tp_as_buffer */
    Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC |
        Py_TPFLAGS_BASETYPE | Py_TPFLAGS_LIST_SUBCLASS, /* tp_flags */
    list___init____doc__,                       /* tp_doc */
    (traverseproc)list_traverse,                /* tp_traverse */
    (inquiry)_list_clear,                       /* tp_clear */
    list_richcompare,                           /* tp_richcompare */
    0,                                          /* tp_weaklistoffset */
    list_iter,                                  /* tp_iter */
    0,                                          /* tp_iternext */
    list_methods,                               /* tp_methods */
    0,                                          /* tp_members */
    0,                                          /* tp_getset */
    0,                                          /* tp_base */
    0,                                          /* tp_dict */
    0,                                          /* tp_descr_get */
    0,                                          /* tp_descr_set */
    0,                                          /* tp_dictoffset */
    (initproc)list___init__,                    /* tp_init */
    PyType_GenericAlloc,                        /* tp_alloc */
    PyType_GenericNew,                          /* tp_new */
    PyObject_GC_Del,                            /* tp_free */
};

tuple 的 PyTypeObject 源碼在 tupleobject.c,程式如下:
PyTypeObject PyTuple_Type = {
    PyVarObject_HEAD_INIT(&PyType_Type, 0)
    "tuple",
    sizeof(PyTupleObject) - sizeof(PyObject *),
    sizeof(PyObject *),
    (destructor)tupledealloc,                   /* tp_dealloc */
    0,                                          /* tp_print */
    0,                                          /* tp_getattr */
    0,                                          /* tp_setattr */
    0,                                          /* tp_reserved */
    (reprfunc)tuplerepr,                        /* tp_repr */
    0,                                          /* tp_as_number */
    &tuple_as_sequence,                         /* tp_as_sequence */
    &tuple_as_mapping,                          /* tp_as_mapping */
    (hashfunc)tuplehash,                        /* tp_hash */
    0,                                          /* tp_call */
    0,                                          /* tp_str */
    PyObject_GenericGetAttr,                    /* tp_getattro */
    0,                                          /* tp_setattro */
    0,                                          /* tp_as_buffer */
    Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC |
        Py_TPFLAGS_BASETYPE | Py_TPFLAGS_TUPLE_SUBCLASS, /* tp_flags */
    tuple_new__doc__,                           /* tp_doc */
    (traverseproc)tupletraverse,                /* tp_traverse */
    0,                                          /* tp_clear */
    tuplerichcompare,                           /* tp_richcompare */
    0,                                          /* tp_weaklistoffset */
    tuple_iter,                                 /* tp_iter */
    0,                                          /* tp_iternext */
    tuple_methods,                              /* tp_methods */
    0,                                          /* tp_members */
    0,                                          /* tp_getset */
    0,                                          /* tp_base */
    0,                                          /* tp_dict */
    0,                                          /* tp_descr_get */
    0,                                          /* tp_descr_set */
    0,                                          /* tp_dictoffset */
    0,                                          /* tp_init */
    0,                                          /* tp_alloc */
    tuple_new,                                  /* tp_new */
    PyObject_GC_Del,                            /* tp_free */
};

我們現將注意力放在其 methods 操作方法:
list 的 methods 操作方法,源碼如下:
static PyMethodDef list_methods[] = {
    {"__getitem__", (PyCFunction)list_subscript, METH_O|METH_COEXIST, "x.__getitem__(y) <==> x[y]"},
    LIST___REVERSED___METHODDEF
    LIST___SIZEOF___METHODDEF
    LIST_CLEAR_METHODDEF
    LIST_COPY_METHODDEF
    LIST_APPEND_METHODDEF
    LIST_INSERT_METHODDEF
    LIST_EXTEND_METHODDEF
    LIST_POP_METHODDEF
    LIST_REMOVE_METHODDEF
    LIST_INDEX_METHODDEF
    LIST_COUNT_METHODDEF
    LIST_REVERSE_METHODDEF
    LIST_SORT_METHODDEF
    {NULL,              NULL}           /* sentinel */
};
而 tuple 的 methods 操作方法,源碼如下:
static PyMethodDef tuple_methods[] = {
    TUPLE___GETNEWARGS___METHODDEF
    TUPLE_INDEX_METHODDEF
    TUPLE_COUNT_METHODDEF
    {NULL,              NULL}           /* sentinel */
};

到此得知了 list 與 tuple 的 methods 之差異,例如 list 可以 sort 排序, 而 tuple 就不能 sort 排序;

以及兩者的特性:
List其特性有:
List is a collection which is ordered and changeable. Allows duplicate members.
且tuple因其特性有:
Tuple is a collection which is ordered and unchangeable. Allows duplicate members.
是ordered順序性與unchangeable(不可改變)的特性。

2018年6月27日 星期三

指標,網路上資訊好亂喔(1-1): 嵌入式 C&C++ 重點語法技術淺談[連載1] at ET-Lab(挖挖挖技術實驗室)

就跟標題一樣,近日,不、應該說長期以來很多網上討論區、論壇、各種影片等等,有時我點看了一下,其講法可能跟我所知道的 ANSI C89、C99或C11[註1],有些差異!!! 搞得我思考好亂喔。
這裡就我明確所知道的,且要完全遵守ANSI C89、C99或C11標準[註1]重要的更要對照C編譯後的組合語言比對一下驗證,這樣才能撥亂反正

首先第一個觀念,因編譯時有最佳化,C的變數不一定會佔記憶體空間,如果加上 & 用以取對應operand的位址時,就會佔記憶體空間。這裡貼出C99的一段敘述:
The unary & operator yields the address of its operand. If the operand has type "type",the result has type "pointer to type"。
程式碼解說:
int a=10;
  &a --> 會取得"pointer to type"的型態,那a 是int, 就會得到 a變數的位址,這位址是 int* 的型態(稍後解說)
 
1. 驗證 因編譯時有最佳化,C的變數不一定會佔記憶體空間:
一Cortex Mx C語言如下,

int fun1( int n1 )
{
  n1++;
  return n1;
}
int main(int a1)
{
  int x1;
  x1 = fun1( 10  );
  return x1+a1;
}

C語言跟組語對照如下,看圖說故事:


看到
0x0800046A F7FFFFF9  BL.W     fun1 (0x08000460), 會跳到0x08000460,

0x08000460 1C40      ADDS     r0,r0,#1
0x08000462 4770      BX       lr,    返回 0x0800046A 下一指令

0x0800046E 4603      MOV      r3,r0
0x08000470 1898      ADDS     r0,r3,r2  <--- 這行是 return x1+a1;

所以驗證了,C的變數不一定會佔記憶體空間,上面情況是以 暫存器 完成,加快執行速度

再用另一種編譯器(網路上online comliler),一C語言如下,
int main(int num)
{
   return num;
}
編譯成組語,就有利用堆疊區記憶體空間了(請參考暫存器 r7 ),看圖說故事:


2018年6月26日 星期二

用C語言擴展Python功能:適用使用時機,要驅動嵌入式Linux新硬體

Python中文維基:Python 直譯器本身幾乎可以在所有的作業系統中執行。Python的正式直譯器CPython是用C語言編寫的、是一個由社群驅動的自由軟體,目前由Python軟體基金會管理。

維基上也說:以 C++ (或 C) 撰寫的模組通常用於延伸 Python 解譯器的功能,以及啟用低階作業系統功能的存取。 及 在操控硬體的場合使用C++(C語言),在快速開發時候使用Python。

因Python是由C語言實作完成的,所以在很多嵌入式系統可以用已經寫好的Python API很容易驅動硬體,但在要增加新硬體,且要能由Python驅動,就一定要先有寫好的硬體驅動程式(C語言),再用C語言擴展Python功能完成類似OS上的AP,用以驅動寫好的新硬體驅動程式(C語言)。
這裡我在ARM板上(任意開發板或樹梅派也行),簡單DEMO一Hello 範例,程式如下:

hellomodule.c

#include <Python.h>
static PyObject* hello_name(PyObject *self, PyObject *args)
{
    const char *name;
    if (!PyArg_ParseTuple(args, "s", &name))
    {
        return NULL;
    } 
    printf("Hello %s!\r\n", name);
    Py_RETURN_NONE;
}
static PyMethodDef HelloMethods[] =
{
    {"hello", hello_name, METH_VARARGS, "Hello Example"},
    {NULL, NULL, 0, NULL}
};
static struct PyModuleDef hello =
{
    PyModuleDef_HEAD_INIT,
    "hello",     /* name of module */
    "",          /* module documentation, may be NULL */
    -1,          /* size of per-interpreter state of the module,
                    or -1 if the module keeps state in global variables. */
    HelloMethods
};
PyMODINIT_FUNC PyInit_hello(void)
{
    return PyModule_Create( &hello );
}

使用以下指令編譯
gcc -fpic --shared $(python3-config --includes) hellomodule.c -o hello.abi3.so

再來準備 setup.py,程式如下:

from setuptools import setup, Extension
setup(
    name='hello',
    version='1.0',
    description='Python Package with Hello World C Extension',
    ext_modules=[
        Extension(
            'hello',
            sources=['hellomodule.c'],
            py_limited_api=True)
    ],
)

接著下安裝指令:
python3 setup.py install

安裝成功後,即可利用Python程式使用剛安裝好 C 的函式庫,
helloworld.py 程式如下:

import hello

def main():
    hello.hello('World')

if __name__ == "__main__":
    main()
 
 
本範例是在Python3.5下完成,接著驗證,下達指令:

python3.5 helloworld.py

成功!!

有關更詳細的Python API的說明,請參考:
Python Documentation contents
https://docs.python.org/3.5/contents.html

有關實作Python 使用 C 的函式庫,更詳細的說明,請參考:
Extending Python with C or C++
https://docs.python.org/3.5/extending/extending.html

執行過程的畫面:



2018年6月12日 星期二

紀錄: 於ARM SoC開發板上使用 WebCam 完成人臉識別,並點亮對應人臉識別的LED燈

完成 使用 WebCam 在 Python 中以 OpenCV 完成人臉檢測後,再接著是 於ARM SoC開發板上使用 WebCam 完成人臉識別,並點亮對應人臉識別的LED燈。影片如下:

這影片是人臉識別DEMO的錄影,程式裡給了歐巴馬、川普的照片,啟動ARM SoC開發板上的程式,當識別到歐巴馬,點亮第一個LED燈。識別到川普,點亮第二個LED燈
其他的人 都以unKnown未知歸類,就點亮第三個LED燈。 要仔細看視頻上方的LED燈顯示喔

有關 紀錄: 使用 WebCam 在 Python 中以 OpenCV 完成人臉檢測 的網址如下:
http://cyh-etlab.blogspot.com/2018/06/webcam-python-opencv.html

2018年6月11日 星期一

由識別手寫數字圖像應用程式(plot_digits_classification.py)範例了解機器學習(Machine Learning)的支持向量機(Support Vector Machine)演算法

現在有很多OSS的機器學習(Machine Learning)相關的Tools,讓非資工相關科系掌握機器學習(Machine Learning)演算法相對容易很多。如Scikit-learn就是開源機器學習的一種框架,Scikit-learn的機器學習基本功能主要是分:監督學習(Supervised learning)、無監督學習(Unsupervised learning)、模型選擇和評估(Model selection and evaluation)、數據集轉換(Dataset transformations)等。

識別手寫數字圖像應用程式是由支持向量機(Support Vector Machines/SVM),對SVM模型進行訓練,由分類、預測完成手寫數字的識別。執行畫面如下:

什麼是支持向量機(Support Vector Machines/SVM)? 簡單的說:SVM用於分類和回歸的相關監督學習方法,現給定一組訓練樣例,每個訓練樣例被標記為屬於兩類別之一,SVM會尋找使用盡可能寬的邊界來分隔類別的界限。倘若如果無法清楚地分隔兩個類別,SVM演算法就會盡量找出最佳界限。如圖示,將綠圓點跟紅圓點用一超平面(hyper-plane)分成兩類:
現透過幾個簡單情景範例說明,要找出正確的超平面(hyper-plane),圖示法式很容易理解的:
情景-1:

有三個超平面(hyper-plane)A,B和C。現在,要找出正確的超平面(hyper-plane)來分類"藍色星型"和"紅色圓"。在這種情景,超平面“B” 完成了這項工作。
情景-2:
有三個超平面(hyper-plane)A,B和C。最近數據點(任一類)和超平面(hyper-plane)之間的距離(邊距)將有助於我們決定正確的超平面(hyper-plane)。

在這種情景,超平面“C” 完成了這項工作。

情景-3:

有些人可能選擇了超平面B,因為它具有比A更高的邊距餘量。這是錯的,所以SVM在最大化邊界之前,要準確地對類別進行分類。在這種情景,超平面“A” 完成了這項工作。

情景-4:


因為其中一個"藍色星型"位於其他(圈子)類的領域,而作為異常點。
SVM具有忽略異常值,並找到具有最大餘量的超平面的特徵。

情景-5:
在上面的情景中,兩個類之間不能有線性超平面,那要如何對這兩個類進行分類?這裡將添加一個新的特徵 z = x^2 + y^2。現在,重新繪製x軸和z軸上的數據點,如下:
如上圖SVM中,這兩個類之間很容易有一個線性超平面了,那在原始的x y平面空間中查看超平面時,它看起來像一個圓圈:

上述的Image圖參考自Analytics Vidhya。在Python中,scikit-learn是實現機器學習算法的廣泛使用的庫,SVM也可以在scikit-learn庫中使用,並遵循相同的結構(導入庫,對象創建,擬合模型和預測)。讓我們看看識別手寫數字圖像應用程式(plot_digits_classification.py)範例的代碼:

print(__doc__)

# Author: Gael Varoquaux <gael dot varoquaux at normalesup dot org>
# License: BSD 3 clause

# Standard scientific Python imports
import matplotlib.pyplot as plt

# Import datasets, classifiers and performance metrics
from sklearn import datasets, svm, metrics

# 數字資料集(The digits dataset)
digits = datasets.load_digits()

# The data that we are interested in is made of 8x8 images of digits, let's
# have a look at the first 4 images, stored in the `images` attribute of the
# dataset.  If we were working from image files, we could load them using
# matplotlib.pyplot.imread.  Note that each image must have the same size. For these
# images, we know which digit they represent: it is given in the 'target' of
# the dataset.
images_and_labels = list(zip(digits.images, digits.target))
for index, (image, label) in enumerate(images_and_labels[:4]):
    plt.subplot(2, 4, index + 1)
    plt.axis('off')
    plt.imshow(image, cmap=plt.cm.gray_r, interpolation='nearest')
    plt.title('Training: %i' % label)

# To apply a classifier on this data, we need to flatten the image, to
# turn the data in a (samples, feature) matrix:
n_samples = len(digits.images)
data = digits.images.reshape((n_samples, -1))

# Create a classifier: a support vector classifier
classifier = svm.SVC(gamma=0.0001)

# We learn the digits on the first half of the digits
classifier.fit(data[:n_samples // 2], digits.target[:n_samples // 2])

# Now predict the value of the digit on the second half:
expected = digits.target[n_samples // 2:]
predicted = classifier.predict(data[n_samples // 2:])

print("Classification report for classifier %s:\n%s\n"
      % (classifier, metrics.classification_report(expected, predicted)))
print("Confusion matrix:\n%s" % metrics.confusion_matrix(expected, predicted))

images_and_predictions = list(zip(digits.images[n_samples // 2:], predicted))
for index, (image, prediction) in enumerate(images_and_predictions[:4]):
    plt.subplot(2, 4, index + 5)
    plt.axis('off')
    plt.imshow(image, cmap=plt.cm.gray_r, interpolation='nearest')
    plt.title('Prediction: %i' % prediction)

plt.show()

上述程式碼,有關SVM的部分,也是這支程式重要的地方如下,含程式注釋:

# 創建一個支持向量機(Support Vector Machine)分類器
classifier = svm.SVC(gamma=0.0001)

# 訓練 SVC 模型
classifier.fit(data[:n_samples // 2], digits.target[:n_samples // 2])

expected = digits.target[n_samples // 2:]
# 預測測試樣本的數字值
predicted = classifier.predict(data[n_samples // 2:])

其中 svm.SVC 的 SVC是個class,原始程式碼在 classes.py, class SVC完整宣告如下(刪掉注釋部分):
class SVC(BaseSVC):
    def __init__(self, C=1.0, kernel='rbf', degree=3, gamma='auto',
                 coef0=0.0, shrinking=True, probability=False,
                 tol=1e-3, cache_size=200, class_weight=None,
                 verbose=False, max_iter=-1, decision_function_shape=None,
                 random_state=None):

        super(SVC, self).__init__(
            impl='c_svc', kernel=kernel, degree=degree, gamma=gamma,
            coef0=coef0, tol=tol, C=C, nu=0., shrinking=shrinking,
            probability=probability, cache_size=cache_size,
            class_weight=class_weight, verbose=verbose, max_iter=max_iter,
            decision_function_shape=decision_function_shape,
            random_state=random_state)
其__init__建構函數的參數有很多,最重要的是 C=1.0, kernel='rbf', degree=3, gamma='auto',我們可以透過調整SVC的參數,以有效地提高此模型性能,這重要參數,“kernel”,“gamma”和“C” 簡單說明如下:
kernel:kernel可用的選項,如“linear”,“rbf”,“poly”等等(默認值是“rbf”)。這裡“rbf”和“poly”對於非線性超平面很有用。
gamma:'rbf','poly'和'sigmoid'的kernel係數。伽馬值越高,將盡力準確地按照訓練數據集進行擬合。
C: 誤差項的懲罰參數C. 它還控制平滑決策邊界和正確分類訓練點之間的權衡。

到此簡單的透過由識別手寫數字圖像應用程式(plot_digits_classification.py)範例了解機器學習(Machine Learning)的支持向量機(Support Vector Machine)演算法。而SVM演算法也是有相關的優點和缺點,
優點是:
1.它工作得很好,分離的邊界很清晰
2.它在高維空間中很有效。
3.它在維數大於樣本數的情況下有效。
4.它使用決策函數中的一個訓練點子集(稱為支持向量),因此它也具有內存效率。
缺點是:
1.如果我們有大量數據,因為所需的訓練時間較長,它表現不佳
2.當數據集有更多的noise 即目標類重疊時,它也表現不佳
3.支持向量機不直接提供概率估計,這些是使用昂貴的 five-fold cross-validation 來計算的。

2018年6月10日 星期日

紀錄: 使用 WebCam 在 Python 中以 OpenCV 完成人臉檢測

這影片是紀錄【Python +OpenCV】Python 3 程式設計最佳入門到進階應用實戰工程師培訓班:基礎至進階語法+影像處理與辨識(OpenCV) 課程的DEMO部分,請參考相關網頁:
 
先決條件是必須先安裝好相關的程式: Python、Opencv。。。。。。

就像 Youtube影片一樣,通過WebCam可以很容易地在Opencv的影像中檢測到臉部,完整Python程式碼如下:

import cv2
import sys
from time import sleep

cascPath = "haarcascade_frontalface_default.xml"
faceCascade = cv2.CascadeClassifier(cascPath)

video_capture = cv2.VideoCapture(0)

while True:
    if not video_capture.isOpened():
        print('Unable to load camera.')
        sleep(5)
        pass

    # Capture frame-by-frame
    ret, frame = video_capture.read()

    gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)

    faces = faceCascade.detectMultiScale(
        gray,
        scaleFactor=1.1,
        minNeighbors=5,
        minSize=(30, 30)
    )

    # Draw a rectangle around the faces
    for (x, y, w, h) in faces:
        cv2.rectangle(frame, (x, y), (x+w, y+h), (0, 255, 0), 2)

    # Display the resulting frame
    cv2.imshow('Video', frame)

    if cv2.waitKey(1) & 0xFF == ord('q'):
        break

    # Display the resulting frame
    cv2.imshow('Video', frame)

# When everything is done, release the capture
video_capture.release()
cv2.destroyAllWindows()

現在我們來解析重點程式部分...

OpenCV已經包含許多預先訓練的分類器(classifiers),臉部常用的是 haarcascade_frontalface_default.xml,所以要先加載所需的XML分類器:

cascPath = "haarcascade_frontalface_default.xml"
faceCascade = cv2.CascadeClassifier(cascPath)

接著要將 WebCam 設定為 OpenCV 的視頻源(video source),這樣 OpenCV 可以擷取WebCam的視頻。
video_capture = cv2.VideoCapture(0)

再來循環執行,持續透過 Opencv 的 read()功能從視頻源中讀取一幀(Frame),及返回 ret, frame:
ret是返回值, frame是實際的視頻幀(Frame)讀取(每個循環一幀(Frame))。
ret, frame = video_capture.read()

再來Opencv 讀進來的影像格式預設為BGR,現將其讀取的影像(frame)灰階化得到gray影像。
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)

Opencv最重要的人臉檢測函數,使用的是 detectMultiScale函數。它可以檢測出圖片中所有的人臉,並將人臉用vector保存各個人臉的座標、大小(用矩形表示),其函數呼叫如下:
    faces = faceCascade.detectMultiScale(
        gray,
        scaleFactor=1.1,
        minNeighbors=5,
        minSize=(30, 30)
    )
detectMultiScale 參數是: gray 是CV_8U類型的矩陣,其中包含檢測對象的圖像。 scaleFactor 是 指定在圖像大小依每個圖像比例縮小的程度。 minNeighbors是指定每個候選矩形必須保留多少個鄰居的參數。此參數將影響檢測到的人臉質量,較高的值會導致檢測數量較少但質量較高。minSize是最小可能的人臉大小。小於此值的人臉將被忽略。

再來是在偵測到的臉部周圍繪製一個矩形。
    # Draw a rectangle around the faces
    for (x, y, w, h) in faces:
        cv2.rectangle(frame, (x, y), (x+w, y+h), (0, 255, 0), 2)

最後將幀(Frame)顯示。
cv2.imshow('Video', frame)

再來 Python 等待'q'鍵被按下。如果偵測到按下 'q',退出Python。
    if cv2.waitKey(1) & 0xFF == ord('q'):
        break

最後是離開Python前,釋放capture 的 video。
# When everything is done, release the capture
video_capture.release()
cv2.destroyAllWindows()

2018年4月15日 星期日

於mbed SDK Porting課堂講解Callback(R (*func)() = 0),而隨手Coding完成C/C++程式對應之解說範例

2018/4/15 課堂進行mbed SDK Porting,講義進行至P51頁的 class Callback<R()>{ ... }; 時,全部的學員對這C++的template、function pointer、struct & union之混和運用,可以說是完全不熟悉,或根本沒見過這些語法。耶。。。學員幾乎都是研究所畢業了,且在業界也工作多年的喔,我上課時口頭調查C與C++熟悉嗎? 幾乎都認為熟悉喔。 不多說,我就利用最後的近十分鐘的時間,順手寫一個簡單的對應範例,一邊寫,一邊講解這些語法,寫完後。。。編譯成功& 執行成功,再問看學員,懂了嗎?? 嗯,至少懂了,但。。還是要回去再多複習: 程式如下:


#include <iostream>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

void addNum( int a,int b){  printf("%d+%d=%d\r\n", a,b,a+b );  }
int addNum2( int a,int b){  return a+b; }
double addNum3( double a,double b){  return a- b; }

template<typename RT,typename argType>
class MATH
{
public:
MATH():personObj(){}
void setFpointer( RT (*funP)(argType,argType) );
union
{
RT (*funP)(argType,argType);
  void* vPointF;
}_pOPF;
struct person
{ person(){ printf("person()\r\n"); strcpy(myName,"CYH"); age=9999;  }
  char myName[8];
  int age;
}personObj;
};
template<typename RT,typename argType>
void MATH<RT,argType>::setFpointer( RT (*funP)(argType,argType) )
{
  _pOPF.funP = funP;
}
int main(int argc, char** argv) {
MATH<void,int> MATHObj;
MATH<int,int> intMATHObj;
MATH<double,double> doubleMATHObj;

MATHObj.setFpointer( addNum );
MATHObj._pOPF.funP( 100,10 );

intMATHObj.setFpointer( addNum2 );
printf( "intMATHObj._pOPF.funP(200,20)=%d\r\n",intMATHObj._pOPF.funP(200,20) );

doubleMATHObj.setFpointer( addNum3 );
printf( "doubleMATHObj._pOPF.funP(200.2,20.2)=%f\r\n",doubleMATHObj._pOPF.funP(200.2,20.2) );
printf( "%s age is %d\r\n", MATHObj.personObj.myName, MATHObj.personObj.age );
system("PAUSE");
return 0;

}

2018年4月6日 星期五

淺談CUDA 平行運算機制之 cudaMemcpy

完成【AI人工智慧/機器、深度學習/平行運算:模組A】 CUDA 平行運算技術與實作實戰班課程訓練,我在課堂上加強強調GPU的運算能力是超級強大,但CUDA是異質多核心系統架構,在CPU是x86架構,GPU是NVIDIA的,採PCIe BUS的架構下,因為PCIe BUS架構 的物理特性限制,一個標準的PCI-E 1.0 X16,匯流排寬度16位元,工作時脈2.5 GHz,其資料速率是8 GB/s(雙工),但實際上通常為5-6GB/s。
一般大多數採CUDA平行運算應用程式,是一開始做一次cudaMalloc,之後不再調用它了。
之後會在x86 Host端跟GPU Kernel端透過cudaMemcpy 在PCIe BUS間互傳資料,所以整體的CUDA平行運算應用程式其瓶頸會在cudaMemcpy函式的呼叫上。 現簡單驗證:
1. 循序執行程式如下,numElements是100000000,程式是將numElements個的float type矩陣相加:
cpu_startTime = clock();
for (int i = 0; i < numElements; ++i)
{
h_C[i] = h_A[i] + h_B[i];
}
cpu_endTime = clock();
執行所花的時間是 Sequential cpu_endTime - cpu_startTime=273.000000。

2. CUDA 平行運算程式如下:
/**
 * CUDA Kernel Device code
 * Computes the vector addition of A and B into C. The 3 vectors have the same
 * number of elements numElements.
 */
__global__ void vectorAdd(const float *A, const float *B, float *C, int numElements)
{
    int i = blockDim.x * blockIdx.x + threadIdx.x;

    if (i < numElements)
    {
        C[i] = A[i] + B[i];
    }
}
GPU 使用 390625 blocks,每個Block 配置 256 threads,測定CUDA 平行運算執行程式執行所花的時間,包含執行GPU CUDA的vectorAdd Kernel程式 與 將結果複製到 x86 Host端,整個所需的時間(PS:如果單測vectorAdd Kernel程式一定是超級快的),程式片段如下:

// Launch the Vector Add CUDA Kernel
cpu_startTime1 = clock();
vectorAdd<<<blocksPerGrid, threadsPerBlock>>>(d_A, d_B, d_C, numElements);
err = cudaMemcpy(h_C, d_C, size, cudaMemcpyDeviceToHost);
cpu_endTime1 = clock();

執行所花的時間是 Parallel cpu_endTime1 - cpu_startTime1=167.000000。
cudaMemcpy要複製的memory size是100000000個的float變數,大約380MByte。
若PCI-E 1.0 X16實際資料速率是5 GB/s,那 1ms大約是5M Byte,一對比下來,瓶頸真的是cudaMemcpy了。
執行畫面如下:


2017年12月28日 星期四

江義華的部落格(cyh.etlab's blog)

這裡記錄了我的點點滴滴!我的email: microcyh@seed.net.tw
(嵌入式 Linux 實戰精修班/8051/C++/嵌入式ARM-Cortex M3系統開發進階班 /Linux dervice driver/FPGA)

2017年12月27日 星期三

1. MEGAWIN笙泉公司 MA92G8A系列專用型 ASSP, ARM Cortex M0 SoC微控制器

MEGAWIN笙泉公司在ARM 32位元系列SoC 微控制器(MCU)市場,針對特定應用領域推出專用型解決方案,以創造與眾不同的產品價值。
笙泉,憑藉著在傳統8051 MCU的優異抗干擾能力及極佳的加密性核心技術上,以ARM Cortex-M0為內核,開發出MA92G8A系列專用型ASSP ( application-specific standard product) MCU。
MA92G8A系列MCU內建多變型防死區脈寬調製模塊( PWM ),快速I/O處理模塊及快速型比較器( ACMP )...等,可快速導入及應用於復雜電機控制系統。另外!專利型DMA傳輸模塊,可支持多種接口顯示屏( TFT-LCD ),可應用於車用/教育/工控/醫療/ ...等中小型顯示面板。


2017年9月4日 星期一

成果紀錄:將uClinux porting 到 STM32F4 Discovery,與執行一支Linux AP 及 執行 lseek 的Linux Device Driver驅動程式

這裡簡單紀錄這一 "將uClinux porting 到 STM32F4 Discovery" 的成果。
參考這些網址: http://www.emcu.it/uCLinux/uCLinux.html 、 https://emcraft.com/products/343, 將 uBoot 、 uClinux、及 ROM file system Image正確燒錄至 STM32F4 Discovery。
硬體連接 透過 USB轉232 連接到STM32F4 Discovery的 PC10/TX, PC11/RX,在HOST端 PC 執行 putty serial console。如下圖:

 

接著 寫一支 Linux AP C1.c(Hello World), 透過 Cross Compiler 編譯成 C1.exe, 之後正確的傳至 在STM32F4 Discovery執行的 uClinux, C1.exe 放在 /var/tmp 目錄。
再接著 寫 uClinux 的驅動範例與搭配的AP程式、驅動程式 Drv1.c 將其編譯成 Drv.ko、AP程式 AP1.c 編譯成 AP1.exe, 之後正確的傳至 在STM32F4 Discovery執行的 uClinux,也是放在 /var/tmp 目錄。
1. 先執行 ./C1.exe 會看到 Hello CYH,
2. 再來測試於 STM32F4 Discovery 上的 uClinux 執行 Linux Device Driver驅動程式: 先 insmod Drv1.ko, 會看到:
Hello, world
name=cyh , age=9999
之後執行 ./AP1.exe, 正確執行, 畫面如下:


2017 9/5成果紀錄:將uClinux porting 到 STM32F4 Discovery,與執行一支Linux AP 及 執行 lseek 的Linux Device Driver驅動程式

這裡簡單紀錄這一 "將uClinux porting 到 STM32F4 Discovery" 的成果。
參考這些網址: http://www.emcu.it/uCLinux/uCLinux.html 、 https://emcraft.com/products/343, 將 uBoot 、 uClinux、及 ROM file system Image正確燒錄至 STM32F4 Discovery。
硬體連接 透過 USB轉232 連接到STM32F4 Discovery的 PC10/TX, PC11/RX,在HOST端 PC 執行 putty serial console。如下圖:


uClinux_STM32F4_0.jpg - 嵌入式


接著 寫一支 Linux AP C1.c(Hello World), 透過 Cross Compiler 編譯成 C1.exe, 之後正確的傳至 在STM32F4 Discovery執行的 uClinux, C1.exe 放在 /var/tmp 目錄。
再接著 寫 uClinux 的驅動範例與搭配的AP程式、驅動程式 Drv1.c 將其編譯成 Drv.ko、AP程式 AP1.c 編譯成 AP1.exe, 之後正確的傳至 在STM32F4 Discovery執行的 uClinux,也是放在 /var/tmp 目錄。


1. 先執行 ./C1.exe 會看到 Hello CYH,
C1.c 程式如下:
#include <stdio.h>
int main()
{
printf( "Hello CYH\r\n" );
return 0;
}


2. 再來測試於 STM32F4 Discovery 上的 uClinux 執行 Linux Device Driver驅動程式: 先 insmod Drv1.ko, 會看到:
Hello, world
name=cyh , age=9999
之後執行 ./AP1.exe, 正確執行, 畫面如下:


uClinux_STM32F4_1.jpg - 嵌入式


 


AP1.c 程式如下:
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
int main( int argc, char* argv[] )//stdin=0 out=1 err=2
{
size_t readLen;
int fd;
char BUF[ 64 ]; // mknod /dev/chrDrv1 c 99 0
fd = open ( "/dev/chrDrv1" , O_RDWR );

write ( fd , (void *)"CYH1234567890ABCDEFGHIJ" , 24 );
lseek ( fd , 0, SEEK_SET );

memset(BUF,0,64);
readLen = read ( fd , (void *)BUF, 3 ); // BUF <- "CYH";
printf( "1. BUF=%s\r\n", BUF );

memset(BUF,0,64);
readLen = read ( fd , (void *)BUF, 10 ); // BUF <- "1234567890";
printf( "2. BUF=%s\r\n", BUF );

memset(BUF,0,64);
readLen = read ( fd , (void *)BUF, 10 ); // BUF <- "ABCDEFGHIJ";
printf( "3. BUF=%s\r\n", BUF );

memset(BUF,0,64);
readLen = read ( fd , (void *)BUF, 10 ); // BUF <- EOF or 0;
printf( "BUF=%s\r\n", BUF );

close ( fd ) ;
return 0;
}


2017年7月6日 星期四

添加新的Cortex Mx SoC[ STM32F系列]到mbed-OS IOT物聯網產品之mbed SDK Porting 移植技術課程

業界常常在做有關物聯網新產品開發時,採用ARM mbed OS物聯網開發平台,然目前 mbed online Compiler官網所提供的各廠家有支援 mbed 之SoC CPU,都是特定的型號。舉例: 意法半導體STMicroelectronics在其 STM32F10x 系列,僅提供STM32F103RB,且其周邊零件也僅是公板(型號: NUCLEO-F103RB)有支援的才有釋出對應的 mbed SDK。但產品開發在眾多因素下,均會有指定特定CPU型號與周邊IC零件。上述狀況需要mbed SDK Porting 移植技術,我在規劃一些技術課程時,是以自己在業界的研發經驗為基礎,輔以閱讀大量技術性文件與消化實作後,所產生的一系列課程。【ARM嵌入式物聯網系列:模組B】
IoT物聯網 mbed SDK Porting 移植技術與開發實戰精修班


即將開課,有興趣的人請參考。此課程內容是能直接在Keil裡面建立 mbed 的離線工程,匯入完整 mbed SDK原始碼,讓工程師完全掌握 mbed 底層技術,避開離線編譯 mbed SDK無法看到完整 mbed SDK原始碼的窘境,能夠隨意設置CPU型號,並有能力加入產品所需的周邊IC的SDK API,來加速物聯網產品的開發。


 

































【課程名稱】【ARM嵌入式物聯網系列:模組B】
IoT物聯網 mbed SDK Porting 移植技術與開發實戰精修班
 
 
★全台首開 結合專題實作 實務導向為主 ★贈送ARM-Cortex M3板子(價值5,000)
【課程代碼】06C004
【上課時間】7/16~8/13,每週日,9:00~18:00,共30小時[前二週上到17:00,後二週上到18:00] 
【課程主旨】RM 跟他的Partners目前已共同釋出具顛覆性的物聯網統一的開發平臺 mbed。該 mbed 物聯網統一的開發平臺是建立基於ARM的微控制器產品的,它可以讓以後物聯網開發者,無需「重複打造輪子」;物聯網產品開發者使用 ARM 架構的晶片、免費使用 mbed OS,然後最終直接基於 mbed SDK 開發應用層就可以,以此來加速開發和縮短產品的開發時程。但是就像 Embedded Linux、Android 一樣,目前 mbed 官網所提供的各廠家的支援 mbed 之CPU,都是特定的型號。舉例: 意法半導體STMicroelectronics在其 STM32F10x 系列,僅提供STM32F103RB,且其周邊零件也僅是公板(型號: NUCLEO-F103RB)有支援的才有釋出對應的 mbed SDK。但產品開發在眾多因素下,均會有指定特定CPU型號與周邊IC零件。本課程講授mbed SDK Porting 移植技術與開發實戰,讓學員能直接在Keil裡面建立 mbed 的離線工程,能夠隨意設置CPU型號,並有能力加入產品所需的周邊IC的SDK API,加速物聯網產品的開發。
【課程特色】1. 採用 ARM Keil工具,匯入完整 mbed SDK原始碼,讓學員完全掌握 mbed 底層技術,避開離線編譯 mbed SDK無法看到完整 mbed SDK原始碼的窘境。
2. 增加多項物聯網進階LAB實戰,以符合業界需求
3. 增加 mbed SDK程式連結網路功能,讓物聯網設備連接上乙太網路
【修課條件】1. 熟悉 C & C++ 語言與數位邏輯
2.建議上過【ARM嵌入式物聯網系列:模組A】嵌入式ARM-Cortex Mx系統開發韌體 實作實戰班
【課程大綱】1. ARM mbed 開發平臺與 mbed SDK 開發應用層
2. mbed SDK porting 移植技術
3. ARM Cortex Mx CMSIS Module
4. mbed HAL and API
5. mbed的 GPIO 數位輸入輸出與GPIO HAL module driver: ★【LAB】LED顯示與按鍵的使用 
6. mbed的 ADC/DAC與ADC/DAC HAL module driver:★【LAB】 ADC和DAC的使用
7. mbed的PWM輸出與PWM OUT API:★【LAB】PWM調光的運用
8. mbed GPIO中斷應用與HAL module driver:★【LAB】mbed中斷 控制運用
9. mbed 時鐘系統與Time HAL module driver:★【LAB】Time時鐘 系統的運用
10. mbed中的RTC與RTC HAL module driver:★【LAB】使用RTC作按 鍵防彈跳的運用
11. mbed UART通訊與UART/USART HAL module driver:★【LAB】 串列通訊綜合應用
12. mbed SPI通訊與SPI HAL module driver:★【LAB】SPI的運用 
13. mbed I2C通訊與I2C HAL module driver:★【LAB】I2C的運用 
14. mbed 電腦網路應用基礎、mbed TCP/UDP應用程式與mbed HTTP應用 程式設計
★【LAB】mbed 網路介面實驗(TCP/IP + Web Server+控制板上硬體 )



 


 






































【上課時數】30 小時
【上課地點】台北分部(台北市博愛路80號3樓)
【主辦單位】財團法人自強工業科學基金會
【課程費用】15500元 (超值優惠價格需送出報名表後,系統發出報名成功回函確認金額。)
【超值優惠】

  • VIP企業會員價:VIP企業會員可享優惠價格 (按我)

  • 會員優惠價: 會員於開課前七天完成報名繳費者可享會員優惠價 14500 元

  • 會員紅利折抵:本課程歡迎使用紅利折抵,最高可使用 200 點


【諮詢專線】02-23113316分機2287 林小姐 HCLin@tcfst.org.tw
【學員須知】報名與繳退費方法| 常見問題與解決會員紅利積點活動辦法
【注意事項】若已報名C003已有板子,可享有不含板子價錢12000(不可再折紅利點數),若不要板子 請務必來電告知02-23113316

  1. 若遇不可預測之突發因素,基金會保有相關課程調整、取消及講師之變動權。

  2. 無紙化環境,輕鬆達到減碳救地球,即日起16小時以上課程結業證書改以電子方式提供。

  3. 使用VIP廠商優惠之學員,上課當日報到時須查核該公司識別證(相關證明資料)。

  4. 會員紅利折抵限以原價或會員優惠價再折抵,其他方案不適用。

  5. 課前請詳閱簡章之課程內容或利用課程諮詢電話。

  6. 課程嚴禁旁聽,亦不可攜眷參與。




 


FPGA Verilog 的學習經驗,提供給要入門的新手

今天簡單說說 FPGA Verilog 的學習經驗,提供給要入門的新手: 1.對自己寫的FPGA Verilog程式,所生成的數位電路要心中有數。 這一點個人認為很重要,就正如寫 C語言,心中要能生成對應的組合語言一樣,我是這樣要求自己的。 雖然 FPGA Verilog語言...