2014/09/29

Setting raspberry pi network

1. Use wpa_cli command connect to ssid


打這個 command 進入 wpa command line interface, (提示符號會變成 ">")

$ wpa_cli

掃瞄目前的wifi訊號, 並且列出結果
> scan
OK
> scan_result
bssid / frequency / signal level / flags / ssid
e4:71:85:04:98:f4       2442    100     [WPA-PSK-TKIP+CCMP][WPA2-PSK-TKIP+CCMP][ESS]    Almond-0326_nomap_5F-7
<3>CTRL-EVENT-SCAN-RESULTS

加入網路ssid, psk是密碼
> add_network
0
> set_network 0 ssid "MYSSID"
> set_network 0 psk "passphrase"
> enable_network 0
<2>CTRL-EVENT-CONNECTED - Connection to 00:00:00:00:00:00 completed (reauth) [id=0 id_str=]

如果是open network
> set_network 0 key_mgmt NONE

把config存起來
> save_config
OK

這些動作會存在
/etc/wpa_supplicant/wpa_supplicant.conf

其它常用的command

status (看看目前的狀態)
help (查看參數與指令)
terminate (關閉wpa_supplicant)
interface (查看有那些無線網卡介面)
list_networks (查看wpa_supplicant.conf檔裡的設定)
select_network (選擇不同的AP,id為AP代號0,1,2,3...)
enable_network 
disable_network 
remove_network (刪除AP資訊)
reconfigure (重新讀取wpa_supplicant.conf設定內容)
save_config (儲存寫入到wpa_supplicant.conf中,否責變更無效)
disconnect (斷線)
reconnect (重新連線)
scan (掃描附近的AP)
scan_results (印出附近AP的相關資訊)


2. search raspberry pi in subnet


用nmap就可以掃, 在windows底下要安裝

假如電腦的ip是 192.168.1.100/24
那麼就下這個command
nmap -sn 192.168.1.0/24

掃出來的結果裡, 可能有
Starting Nmap 6.40 ( http://nmap.org ) at 2014-03-10 12:46 GMT
Nmap scan report for hpprinter (192.168.1.2)
Host is up (0.00044s latency).
Nmap scan report for Gordons-MBP (192.168.1.4)
Host is up (0.0010s latency).
Nmap scan report for ubuntu (192.168.1.5)
Host is up (0.0010s latency).
Nmap scan report for raspberrypi (192.168.1.8)
Host is up (0.0030s latency).
Nmap done: 256 IP addresses (4 hosts up) scanned in 2.41 seconds

可以看到裡面有raspberrypi

NFC NDEF message format

1. NDEF message format


下圖是 NDEF mssage format 的示意圖, 1份 NDEF message 包含 >= 1 NDEF Record
1份 NDEF Record 包含 Header & Payload
Header包含好幾個欄位, 包括 TNF (Type Name Format)



1.1 Record Layout


每個 Record 前5個 bit 定義了record 在message中的位置

  • MB (Message Begin) : True when this is the first record in a message.
  • ME (Message End) : True when this is the last record in a message.
  • CF (Chunk Flag) : True when this record is chunked. (只用在NDEF message大到無法一次傳完)
  • SR (Short Record) : True if the short record format is used for payload length.
  • IL (ID Length is present) : True if the ID Length field is present.

如果 message 有多個 record, 那麼第一個 record 的 MB=1, ME = 0, 最後一個 record 的 MB = 0, ME = 1
如果 message 只有1個 record, 那個這個 record 的 MB=1, ME = 1

1.2 Type Name Format


TNF (Type Name Format) 佔 3 bits, 值為 0~7

  • 0 Empty : Empty record that has no type or payload
  • 1 Well-Known : NFC Forum RTD (Record Type Definition) specification 定義好的其中1種
  • 2 MIME media-type : Internet media type (defined in RFC 2046 )
  • 3 Absolute URI : URI (defined in RFC 3986 )
  • 4 External : User-defined value, based on rules in NFC Forum RTD specification
  • 5 Unknown : Type is unknown. Type length must be 0.
  • 6 Unchanged : Only for middle and terminating records of chunked payloads. Type length must be 0.
  • 7 Reserved : Reserved by the NFC Forum for future use.

常見的應用程式裡, TNF 01 (Well-Known) 以及 TNF 02 (MIME media-type) 最為常用
另外, TNF 04 (External) 也很常用, 包括了 Android App

1.3 Record Type Length


在 TNF & Layout Flag 之後是 1 byte 的 Payload Type Length
它定義了 Record Type 的長度
Record Type的用途是在TNF之外提供更細的分類, 而這個欄位的長度是可變動的
而 Record Type Length 定義這個欄位的長度

1.4 Payload Length


接著是 Payload Length, 它可能佔用1個 byte 或佔用 4個 byte
如果前面的 SR (Short Record) = 1, 那麼它佔用1個 byte
如果 SR = 0, 那麼它佔用4個byte

1.5 Record Type


Record Type,  RTD (Record Type Definition)進一步描述 Payload的內容, 這個

比如說, 當 TNF = 01 (Well-Known), 那麼 Record Type = "T" 表示 text, "U" 表示 URI, "Sp" 表示 Smart poster,
當 TNF = 02 (MIME media-type), 那麼 Record Type 可以描述 "text/html," "text/json," 以及 "image/gif."
當 TNF = 04 (External), Record Type 可以描述它是 Android APP

1.6 ID length &  ID (optional)


如果前面的 flag IL(ID Length is present)=1, 那麼就會有 ID length 以及 ID 欄位

Payload ID 是 optional field, 通常是 URL, 但也可以隨便放, 它的用途是讓應用程式提供相關資訊, 或是讓不同的 NDEF record reference 其它 record
因為是 optional, 所以也可以不放

1.7 Payload


Payload 沒有規定它的 format, 通常 NDEF library 在包NDEF record時, 是直接塞data進去而不做檢查, 所以可以加密, 也可以帶二進位資料












NFC Introduction

1. Introduction


RFID (Radio Frequency IDentification)

  • 通常用於 identification, 而不是 communication
  • RFID tags 可以帶少量的 data, 通常小於1000 bytes


NFC (Near Field Communication)

  • 交換更複雜的資料
  • NFC reader 可以讀 passive RFID tags, 也可以寫入部份的memory, 或是寫入標準格式的 RFID tags
  • 有效距離 <= 10cm, 低功耗, 其它裝置的干擾較小
  • 傳輸速率比wifi/bt低, 不適合需要高速傳輸的用途



2. RFID operating


Acting rules in RFID exchange:

  • initiator: 可能是 tag reader, 或是 tag reader/writer device
  • target: 也就是 tag, 當 initiator 嘗試連線時回應 UID (Unique IDentifier number)

Communication modes:

  • Passive RFID exchange: 用於 reader/writer 和 tag, 板上沒有power, 利用 radio power 回應 data
  • Active RFID exchanges: 用於接電的 reader/writer, 有效距離較長


RFID tag 在板上有少量的 memory <= 1KB,
initiator device 可以讀上面的 data
如果是 reader/writer device, 除了讀 data, 也可以寫 data 到 memory 上
(通常用於紀錄剩下多少錢之類的, 但現在都把database建在網路上, 然後用UID在網路找這筆資料)

ISO 制定了 很多 RFID 標準
不同的標準裡定義了 radio frequency, data transfer rate, data formats,...


3. NFC operating


NFC 和 RFID 一樣有 initiator & target
通常 target 是 programmable device, 可以做複雜的操作之後再回傳 data

Communication modes in NFC

  • passive communication mode: initiator 持續供電, 使 target 持續從 initiator 那邊吃電
  • active communication mode: initiator & target 都有電源

Operating modes in NFC

  • reader/writer, 可對 targer 讀寫
  • card emulator, 當它位於其它 NFC/RFID device field 裡
  • peer-to-peer mode, 可彼此交換 data


4. NFC data format


NFC devices 交換 data 的格式是 NDEF (NFC Data Exchange Format)
一份 NDEF message 包含 >=1 NDEF record
每個 NDEF record 包含 record type, a unique ID, length, payload
有幾個知名的 record type 是每個 NFC device 應該都會實作的:

  • Simple text records: 送text, 包括metadata像是language, encoding scheme, 通常不包含要求 target 要做的事
  • URIs: 送 network address, 通常預期 target 會送到對應的app, 像是browser
  • Smart Posters: 可以帶更多的 data, 像是 URIs, text, 通常 target 收到 Smart Poster record 會開啟 browser, SMS, email ap之類的
  • Signatures: 提供 trustworthy information

NDEF 是 NFC 與 RFID 最大的不同, 雖然 NFC 和許多 RFID protocol 都操作在 13.56 MHz
但 RFID protocol 沒有 common data format


5. NFC architecture




6. NFC Tag Types


NFC forum 定義了4種 NFC Tag Types:

Type 1

  • Based on ISO-14443A specification.
  • Can be read-only, or read/write capable.
  • 96 B ~ 2 KB memory.
  • Communication speed 106Kb.
  • No data collision protection.
  • Examples: Innovision Topaz, Broadcom BCM20203.


Type 2

  • Based on NXP/Philips Mifare Ultralight tag (ISO-14443A) specification.
  • Can be read-only, or read/write capable.
  • 96 B ~ 2 KB memory.
  • Communication speed 106Kb.
  • Anti-collision support.
  • Example: NXP Mifare Ultralight.


Type 3

  • Based on the Sony FeliCa tags (ISO-18092 and JIS-X-6319-4), without the encryption and authentication support that FeliCa affords.
  • Configured by factory to be read-only, or read/write capable.
  • Variable memory, up to 1MB per exchange.
  • Two communication speeds, 212 or 424Kbps.
  • Anti-collision support.
  • Example: Sony FeliCa.


Type 4

  • Based on NXP DESFire tag (ISO-14443A) specification.
  • Configured by factory to be read-only, or read/write capable.
  • 2, 4, or 8 KB of memory.
  • Variable memory, up to 32KB per exchange.
  • Three communication speeds: 106, 212, or 424Kbps.
  • Anti-collision support.
  • Example: NXP DESFire, SmartMX-JCOP.

另外一種雖然不在 NFC 定義裡, 但已經是目前最常見的 NFC tag type

Mifare Classic tag (ISO–14443A)

  • Memory options: 192, 768, or 3,584 bytes.
  • Communication speed 106 Kbps
  • Anti-collision support.
  • Examples: NXP Mifare Classic 1K, Mifare Classic 4K, Mifare Classic Mini.




2014/09/24

FreeRTOS queue management

1. Introduction


queue 用於 task 之間的溝通, 每個queue能放有限的item
item的大小稱為size, item的數量稱為length
當 queue 被建立時, length & size都會設定好

通常 queue 使用 FIFO, 資料從tail寫入, 從head讀出, (但也有可能從head寫入)
寫入的時候, 會copy data到queue裡面
讀出的時候, 會copy data出來, 並且queue的那一份會刪掉

queue 本身就像個物件, 會自行管理queue的相關功能,
所以多個task可以同時寫入同一個queue, 也可以多個task讀出同一個queue
queue通常有多個writer, 但不常有多個reader
( 這裡的writer指的是嘗試寫入queue的接口, reader指的是嘗試從queue讀出的接口 )

每當 task 嘗試從 queue裡讀出data, 需要設定 block time, 這是因為如果 queue 是空的, task就會進入block state, 直到 bloke time timeout 而回到 ready state, 或者是有其它 task / interrupt 寫入data到queue裡, 這樣也會讓 task 回到 ready state

如果 queue 有多個 reader, 並且有多個 task 都在等空的 queue, 那麼當有新的 data 進來, 只會有一個 task變成 ready state, queue會選出priority最高的task, 如果有多個task有一樣的priority, 那麼會選等最久的

當 task 嘗試寫入 data 到 queue裡, 也需要設定 block time, 這是因為如果 queue 滿了, task就會進入 block state, 直到 block time timeout 而回到 ready state, 或者這期間 queue 騰出空間而讓task回到 ready state

如果 queue 有多個 writer, 並且有多個 task 嘗試寫入滿的 queue, 那麼當空間騰出來時, priority最高的task會變成ready state, 如果有多個 task 有一樣的 priority, 那麼會選等最久的

2. using a queue


2.1 create a queue


xQueueHandle xQueueCreate( unsigned portBASE_TYPE uxQueueLength,
                           unsigned portBASE_TYPE uxItemSize
                           );

其中

  • uxQueueLength : max queue length
  • uxItemSize : item size, 單位是 byte

如果 RAM size 不夠,就會回傳 NULL,
如果成功, 回傳 queue handle

2.2 send data to queue


portBASE_TYPE xQueueSendToBack( xQueueHandle xQueue,
                                const void * pvItemToQueue,
                                portTickType xTicksToWait
                                );

xQueueSendToBack() 會把 item 放到 queue 的後面 (tail), xQueueSend() 跟 xQueueSendToBack() 是同樣的function

portBASE_TYPE xQueueSendToFront( xQueueHandle xQueue,
                                 const void * pvItemToQueue,
                                 portTickType xTicksToWait
                                 );

xQueueSendToFromt 把 data 送到 queue 的前端 (head)

其中它的參數 :

  • xTicksToWait : 如果想讓 send 一直等下去, 可以把 xTicksToWait 設成 portMAX_DELAY, 如果不想等, 就把 xTicksToWait 設成 0

可能的回傳值 :

  • pdPASS : 表示成功寫入, 即使task是在block time timeout之前寫入也算是成功寫入
  • errQUEUE_FULL : 失敗, 如果 xTicksToWait > 0, 那麼表示在 block time timeout之前都沒辦法寫入

2.3 receive data from queue


portBASE_TYPE xQueueReceive( xQueueHandle xQueue,
                             const void * pvBuffer,
                             portTickType xTicksToWait
                             );
xQueueReceive 從 queue head 讀一個 item, 並把 queue 裡的 item 刪掉

portBASE_TYPE xQueuePeek( xQueueHandle xQueue,
                          const void * pvBuffer,
                          portTickType xTicksToWait
                          );

xQueuePeek 從 queue head 讀一個item, 但不會刪掉 item

可能的回傳值 :

  • pdPASS : 表示成功讀出, 即使 task 在 block time timeout 之前讀出也算成功讀出
  • errQUEUE_EMPTY : 失敗, 如果 xTicksToWait > 0, 表示在 block time timeout 之前沒辦法讀出

2.4 query queue


unsigned portBASE_TYPE uxQueueMessagesWaiting( xQueueHandle xQueue );

uxQueueMessagesWaiting 可以拿到目前 queue 裡的 items 數量

要注意的是, 這個不能在interrupt裡面使用,
interrupt應該使用 uxQueueMessagesWaitingFromISR()

3. sample code


/* 宣告一個queue */
xQueueHandle xQueue;

static void vSenderTask( void *pvParameters )
{
    long lValueToSend;
    portBASE_TYPE xStatus;

    /* 我們把要寫入 queue 的資料從 task 的 parameter 帶入, 型態為 long */
    lValueToSend = ( long ) pvParameters;

    for( ;; )
    {
        /* 把data寫入queue, 第1個參數是要寫入的queue,
           第2個參數是要寫入的item pointer, 第3個參數是block time */
        xStatus = xQueueSendToBack( xQueue, &lValueToSend, 0 );
        if( xStatus != pdPASS )
        {
            vPrintString( "Could not send to the queue.\r\n" );
        }

        /* taskYIELD() 通知 scheduler switch 到別的task */
        taskYIELD();
    }
}

static void vReceiverTask( void *pvParameters )
{
    long lReceivedValue;
    portBASE_TYPE xStatus;
    const portTickType xTicksToWait = 100 / portTICK_RATE_MS;

    for( ;; )
    {
        /* 檢查 queue 裡面的 item 數量 */
        if( uxQueueMessagesWaiting( xQueue ) != 0 )
        {
            vPrintString( "Queue should have been empty!\r\n" );
        }

        /* 從 queue 裡讀出data, 第1個參數是要讀出data的queue,
           第2個參數是放 data 的 pointer, 第3個參數是 block time */
        xStatus = xQueueReceive( xQueue, &lReceivedValue, xTicksToWait );
        if( xStatus == pdPASS )
        {
            /* 成功讀出 data, 把它印出來 */
            vPrintStringAndNumber( "Received = ", lReceivedValue );
        }
        else
        {
            /* 100ms內沒有讀出data */
            vPrintString( "Could not receive from the queue.\r\n" );
        }
    }
}

int main( void )
{
    /* 建 queue, 設定成長度為5個item, item大小為 sizeof(long) */
    xQueue = xQueueCreate( 5, sizeof( long ) );
    if( xQueue != NULL )
    {
        /* 建2個 task, 分別對 queue 寫入 data, 
           data 來自於 task 的 parameter, 也就是100和200 */
        xTaskCreate( vSenderTask, "Sender1", 1000, ( void * ) 100, 1, NULL );
        xTaskCreate( vSenderTask, "Sender2", 1000, ( void * ) 200, 1, NULL );

        /* 建1個 task 從 queue 讀出 data, 它的priority比較高, 
           但是在 xQueueReceive(), 如果 queue 裡沒 data, 它就會進 block state */
        xTaskCreate( vReceiverTask, "Receiver", 1000, NULL, 2, NULL );

        vTaskStartScheduler();
    }
    else
    {
        /* 建 queue 失敗 */
    }

    /* we should never reach here */
    for( ;; );
}

4. store compound data


有時候對 receiver task 來說, 它需要知道 sender 是誰, 我們可以把 data type 設計成 struct, 並且裡面加上 sender information

另一種情況是, 如果我們要在 queue 裡放大的 data, 改成用指向該 data 的 pointer 會比較好, 這樣可以避免在寫入和讀出時 copy 大量的 data, 但是要注意 memory 的 allocation & release,




2014/09/22

FreeRTOS task management

1. Task function


在 FreeRTOS 裡, 每條thread都稱作 task, 實作成C function, 回傳值是void, 並且parameter為void pointer, 像這樣
void ATaskFunction( void *pvParameters );

每個task通常會一直執行不會停 (所以應該有個loop在裡面), FreeRTOS 不准有"return", 也不准執行到最後一行

如果我們不需要某個task, 我們應該刪除這個task

底下是 task function可能的實作

void ATaskFunction( void *pvParameters )
{
    /* 如同一般的function來宣告變數 */
    int iVariableExample = 0;

    /* 實作一個 infinite loop */
    for( ;; )
    {
        /* task實際上要做的事 */
    }
    /* 如果task在loop中break出來, 那麼task必需在到function end之前刪除,
       比如說用 vTaskDelete( NULL ), 裡面的NULL表示是刪目前的task */
    vTaskDelete( NULL );
}

task function 可以拿來建多個task, 每個task都是獨立的instance, 有自己的stack

2. Task state


我們先簡單的把task分成Running / Not Running
當 task 從 Not Running 變成 Running, 稱作 task in
相反地稱作 task out

3. Create Task


建task用到 xTaskCreate() API, 它的prototype如下

portBASE_TYPE xTaskCreate( pdTASK_CODE pvTaskCode,
                           const signed portCHAR * const pcName,
                           unsigned portSHORT usStackDepth,
                           void *pvParameters,
                           unsigned portBASE_TYPE uxPriority,
                           xTaskHandle *pxCreatedTask
);
( task.h )

底下是參數的說明:

  • pvTaskCode : 放task function的function pointer
  • pcName : task的名字, 在FreeRTOS不會用到它, 這只是用於debug用途, 名字長度限制為configMAX_TASK_NAME_LEN
  • usStackDepth : 每個 task 被放進 stack 的時候, usStackDepth表示它需要的最大stack space, usStackDepth以byte為單位, 比如說, stack size = 32 bit, usStackDepth = 100, 則會用到100*4=400 bytes stack space, 如果task只是設計成idle task, 那麼可以用這個值 configMINIMAL_STACK_SIZE
  • pvParameters : task function的參數, 型態為 (void *)
  • uxPriority : task 的優先權, 0最低, (configMAX_PRIORITIES – 1)最高
  • pxCreatedTask : 這個task的reference, 用於動態調整這個task的priority, 或是刪掉這個task

可能的回傳值 :

  • pdTRUE : 表示成功
  • errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY : 表示RAM不夠了
底下是sample code:

void vTask1( void *pvParameters )
{
    const char *pcTaskName = "Task 1 is running\r\n";
    volatile unsigned long ul;

    /* As per most tasks, this task is implemented in an infinite loop. */
    for( ;; )
    {
        /* Print out the name of this task. */
        vPrintString( pcTaskName );

        /* Delay for a period. */
        for( ul = 0; ul < mainDELAY_LOOP_COUNT; ul++ )
        {
            /* busy delay */
        }
    }
}

void vTask2( void *pvParameters )
{
    const char *pcTaskName = "Task 2 is running\r\n";
    volatile unsigned long ul;

    /* As per most tasks, this task is implemented in an infinite loop. */
    for( ;; )
    {
        /* Print out the name of this task. */
        vPrintString( pcTaskName );

        /* Delay for a period. */
        for( ul = 0; ul < mainDELAY_LOOP_COUNT; ul++ )
        {
            /* busy delay */
        }
    }
}

int main( void )
{
    /* 建tasks1, 通常需要檢查回傳值, 這裡先略過 */
    xTaskCreate( vTask1,   /* task function pointer, 通常用function name就可以 */
                 "Task 1", /* Task name */
                 1000,     /* Stack depth - 一般的例子不會用到這麼多 */
                 NULL,     /* 不帶parameter */
                 1,        /* priority */
                 NULL );   /* 不需要task handle */

    /* task2跟task1是差不多的 */
    xTaskCreate( vTask2, "Task 2", 1000, NULL, 1, NULL );

    /* 啟動scheduler, 讓tasks開始執行 */
    vTaskStartScheduler();

    /* 正常來說不會執行到這邊, 因為scheduler會執行tasks,
       執行到這邊可能是 heap memory 不夠 */
    for( ;; );
}

實際執行結果會看到task1 & task2兩個不停地task in/out

前個例子是把兩個task在main裡面create, 我們也可以把task放在task裡面create

void vTask1( void *pvParameters )
{
    const char *pcTaskName = "Task 1 is running\r\n";
    volatile unsigned long ul;

    /* 執行到這邊表示Scheduler已經開始執行task1 */
    xTaskCreate( vTask2, "Task 2", 1000, NULL, 1, NULL );

    for( ;; )
    {
        /* Print out the name of this task. */
        vPrintString( pcTaskName );

        /* Delay for a period. */
        for( ul = 0; ul < mainDELAY_LOOP_COUNT; ul++ )
        {
            /* busy delay */
        }
    }
}

因為task1 & task2很像, 所以改寫成一個就好

void vTaskFunction( void *pvParameters )
{
    char *pcTaskName;
    volatile unsigned long ul;

    /* 把要印出來的字串經由parameter帶進來, 再轉成 (char *) */
    pcTaskName = ( char * ) pvParameters;

    for( ;; )
    {
        /* 把perameter帶進來的字串印出來 */
        vPrintString( pcTaskName );

        /* Delay for a period. */
        for( ul = 0; ul < mainDELAY_LOOP_COUNT; ul++ )
        {
            /* busy delay*/
        }
    }
}

/* 要帶給task的字串 */
static const char *pcTextForTask1 = “Task 1 is running\r\n”;
static const char *pcTextForTask2 = “Task 2 is running\t\n”;

int main( void )
{
    /* Create task 1 */
    xTaskCreate( vTaskFunction,         /* function pointer */
                 "Task 1",              /* task name */
                 1000,                  /* stack depth */
                 (void*)pcTextForTask1, /* 參數, 把要印的字串轉成(void *) */
                 1,                     /* priority */
                 NULL );                /* task handle */

    /* Create task2, 參數不一樣 */
    xTaskCreate( vTaskFunction, "Task 2", 1000, (void*)pcTextForTask2, 1, NULL );

    /* Start scheduler */
    vTaskStartScheduler();

    for( ;; );
}

4. Task priority


priority可以動態的改, 最高不會超過configMAX_PRIORITIES (defined in FreeRTOSConfig.h)
我們可以改configMAX_PRIORITIES, 但改的愈高也就耗掉更多的RAM, 所以通常會保留default值

為了選出下一個要執行的task, scheduler應該要在task執行的片段最後面執行
而 tick interrupt 就是讓scheduler達到這個目的
time slice的常度定義在 configTICK_RATE_HZ (FreeRTOSConfig.h)
舉例來說, 如果 configTICK_RATE_HZ =100Hz, 那麼time slice = 10ms

要注意的是, FreeRTOS的時間是以tick當單位, 就連 portTICK_RATE_MS 也是以tick當單位


每次選task, 都會選priority最高的task, 如果有一樣的priority則會輪流

如果我們把剛剛的例子改一下priority的部份

int main( void )
{
    /* task1 的 priority 是 1 */
    xTaskCreate( vTaskFunction, "Task 1", 1000, (void*)pcTextForTask1, 1, NULL );

    /* task2 的 priority 是 2 */
    xTaskCreate( vTaskFunction, "Task 2", 1000, (void*)pcTextForTask2, 2, NULL );

    vTaskStartScheduler();
    return 0;
}

執行結果會看到:
Task 2 is running
Task 2 is running
Task 2 is running
Task 2 is running
Task 2 is running
.....
結果Task 1因為priority比Task 2, 所以每次選都選不到它造成starvation


5. Not running state


為了避免starvation造成某些task永遠跑不到, 可以使用event driven
這意味著在選擇task的時候, scheduler沒有選priority最高的task

5.1 Blocked State


當task在等某個event的時候, 稱它在 Blocked State
task等的event分兩種 :

  • Temporal event : 在等delay或是確切的時間
  • Synchronization event : 這個 event 由其它 task 或 interrupt 產生

task 也可以同時等這兩種, 比如說, 在10s內等待data

5.2 Suspended State


當 task 在 suspended state 的時候, 就不會被scheduler選上
進suspend : vTaskSuspend()
離開suspend : vTaskResume() or xTaskResumeFromISR()
但大多時候不太會用到suspend

5.3 Ready State


當 task為 Not running, 並且不是Blocked/Suspended State, 就稱作 Ready State

5.4 State Transition Diagram



5.4 vTaskDelay()


之前的例子, 用的是busy delay, 所以Task 2實際上並沒有suspend
我們可以用 vTaskDelay() 來改寫這個 delay 讓 Task 2 進 suspend

void vTaskFunction( void *pvParameters )
{
    char *pcTaskName;

    pcTaskName = ( char * ) pvParameters;
    for( ;; )
    {
        vPrintString( pcTaskName );

        /* 等250 ms, 參數是ticks, 可以利用portTICK_RATE_MS把milliseconds轉成ticks */
        vTaskDelay( 250 / portTICK_RATE_MS );
    }
}

實際結果是:
Task 2 is running
Task 1 is running
Task 2 is running
Task 1 is running
Task 2 is running
Task 1 is running
.....

因為有足夠的時間, 所以雖然Task2的priority比較高, 但它執行完就suspend, 讓Task 1在Task 2 suspend的期間成為唯一的task可以執行, 然後suspend

5.5 vTaskDelayUntil()


vTaskDelayUntil() 跟vTaskDelay()很像, 差別是vTaskDelayUntil()決定多少個tick之後要被離開blocked state, 適合用在需要定時執行的task

這是它的prototype

void vTaskDelayUntil( portTickType * pxPreviousWakeTime, portTickType xTimeIncrement );

其中它的參數:

  • pxPreviousWakeTime : 這個pointer會更新上一次離開block state的時間
  • xTimeIncrement : 週期, 單位為ticks, 一樣可以用 portTICK_RATE_MS 轉換成ms
我們再改寫剛剛的例子

void vTaskFunction( void *pvParameters )
{
    char *pcTaskName;
    portTickType xLastWakeTime;

    pcTaskName = ( char * ) pvParameters;

    /* xLastWakeTime 需要初始化成目前的tick count */
    xLastWakeTime = xTaskGetTickCount();

    for( ;; )
    {
        vPrintString( pcTaskName );

        /* 自 xLastWakeTime 的時間等 250ms 離開 block state */
        vTaskDelayUntil( &xLastWakeTime, ( 250 / portTICK_RATE_MS ) );
    }
}

6. Idle task


Processor通常都需要執行些什麼, 如果task都進suspend, 那裡它會執行idle task
idle task 是在 scheduler 一開始的時候產生, 擁有priority 0, 做的事情就跟busy loop差不多

6.1 Idle Task Hook Function


我們也可以加 callback function 在 idle task 裡
通常用於:

  • 執行低優先權的工作, 或只需要背景處理的工作
  • 計算剩下的processing capacity
  • 進low power mode

6.2 Limitation of Idle Task


不可以block / suspend
如果 callback function 呼叫 vTaskDelete() 來刪除某個task, 那麼會馬上跳回這個task, 讓這個task能夠釋放已用的資源

6.3 Usage of callback of Idle Task


callback function的prototype如下

void vApplicationIdleHook( void );

使用的時候要注意 USE_IDLE_HOOK 的config要設成1 (FreeRTOSConfig.h)

底下是sample code:

/* 儲存 ilde task 裡的 loop 次數 */
unsigned long ulIdleCycleCount = 0UL;

/* Idle hook function只能叫做 vApplicationIdleHook() */
void vApplicationIdleHook( void )
{
    ulIdleCycleCount++;
}

void vTaskFunction( void *pvParameters )
{
    char *pcTaskName;

    pcTaskName = ( char * ) pvParameters;
    for( ;; )
    {
        /* 印出 task name 以及 ulIdleCycleCount */
        vPrintStringAndNumber( pcTaskName, ulIdleCycleCount );

        /* Delay 250 ms*/
        vTaskDelay( 250 / portTICK_RATE_MS );
    }
}

執行結果像這樣:
Task 2 is running
uIdleCycleCOunt = 0
Task 1 is running
uIdleCycleCOunt = 0
Task 2 is running
uIdleCycleCOunt = 3869504
Task 1 is running
uIdleCycleCOunt = 3869504
Task 2 is running
uIdleCycleCOunt = 8564623
Task 1 is running
uIdleCycleCOunt = 8564623
......

7. Changing task priority


更改priority :

void vTaskPrioritySet( xTaskHandle pxTask, unsigned portBASE_TYPE uxNewPriority );

pxTask 是 task handle, uxNewPriority 是新的 priority

取得priority :

unsigned portBASE_TYPE uxTaskPriorityGet( xTaskHandle pxTask );

底下是 sample code

/* 拿來存task2 handle */
xTaskHandle xTask2Handle;

void vTask1( void *pvParameters )
{
    unsigned portBASE_TYPE uxPriority;

    uxPriority = uxTaskPriorityGet( NULL );
    for( ;; )
    {
        vPrintString( "Task1 is running\r\n" );

  /* 把 Task 2 的 priority 設成 Task 1 多 1 */
        vTaskPrioritySet( xTask2Handle, ( uxPriority + 1 ) );
    }
}

void vTask2( void *pvParameters )
{
    unsigned portBASE_TYPE uxPriority;

    uxPriority = uxTaskPriorityGet( NULL );
    for( ;; )
    {
        vPrintString( "Task2 is running\r\n" );

        /* 把 Task 2 的 priority 降 2 */
        vTaskPrioritySet( NULL, ( uxPriority - 2 ) );
    }
}

int main( void )
{
    xTaskCreate( vTask1, "Task 1", 1000, NULL, 2, NULL );
    xTaskCreate( vTask2, "Task 2", 1000, NULL, 1, &xTask2Handle );

    vTaskStartScheduler();
    for( ;; );
}

因為Task 2的priority在一開始比Task 1少1,
所以這個sample裡, Scheduler會讓Task 1執行 (並把Task 2的priority設成比Task 1大1)
在下一輪, Scheduler發現Task 2的priority比Task 1大, 就執行Task 2 (並降priority比Task 1小1)
執行結果就是Task 1/2輪流地執行

8. Deleting Task


用 vTaskDelete() API 來刪掉task, 被刪的task就再也不會進到 Running state
而 idle task 負責釋放被刪掉的task的資源, 注意不可大量用 vTaskDelete() 造成 idle task starve
並且要注意只有 kernel 分配給 task 的資源會自動釋放, 但如果是 task 主動要的資源則要在刪除前釋放

底下是 prototype

void vTaskDelete( xTaskHandle pxTaskToDelete );

底下是sample code

/* 存 Task 2 handle */
xTaskHandle xTask2Handle;

void vTask2( void *pvParameters )
{
    vPrintString( "Task2 is running and about to delete itself\r\n" );
    vTaskDelete( xTask2Handle );
}

void vTask1( void *pvParameters )
{
    const portTickType xDelay100ms = 100 / portTICK_RATE_MS;
    for( ;; )
    {
        vPrintString( "Task1 is running\r\n" );

        xTaskCreate( vTask2, "Task 2", 1000, NULL, 2, &xTask2Handle );

        vTaskDelay( xDelay100ms );
    }
}

int main( void )
{
    xTaskCreate( vTask1, "Task 1", 1000, NULL, 1, NULL );
    vTaskStartScheduler();
    for( ;; );
}

Task 1會create Task 2, Task 2在執行完後就會自刪, 所以執行結果就是Task 1/2輪流執行



2014/09/19

svn

Show unversioned files

svn在commit的時候, 預設不會出現新加的files

如果是TortoiseSVN, 在commit視窗左下角有個"Show unversioned files", 將它勾選, 就會出現新增的files

如果是command line, 用這個command就會顯示所有新增的files

svn status | grep ^?








2014/09/16

Netlink

1. Introduction

Netlink 提供 userspace & kernel 之間的溝通, 它定義在 RFC 3549 "Linux Netlink as an IP Services Protocol", 相較於其它溝通方式, 它有這些優點:

  • 不用 polling
  • 可以從 kernel 當作訊息發起端
  • 提供 multicast
kernel 裡相關的檔案:

  • Netlink core files
    • net/netlink/af_netlink.c
    • include/linux/netlink.h
    • net/netlink/genetlink.c
  • Route netlink files
    • net/core/rtnetlink.c
    • include/linux/rtnetlink.h
  • Generic netlink files
    • net/netlink/genetlink.c
    • include/linux/genetlink.h

2. Create netlink socket

userspace & kernel 在create socket 時, 最後都會 call __netlink_create(), 唯一的差別是, kernel會加上屬性 NETLINK_KERNEL_SOCKET


2.1 create netlink socket in userspace


底下是sample code

int sockfd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);

struct sockaddr_nl addr;
bzero (&addr, sizeof(addr));
addr.nl_family = AF_NETLINK;
addr.nl_groups = RTMGRP_IPV4_ROUTE;

bind(sockfd,(struct sockaddr *)&addr,sizeof(addr);

其中 sockaddr_nl 的定義是

struct sockaddr_nl {
    __kernel_sa_family_t  nl_family;  /* AF_NETLINK */
    unsigned short        nl_pad;     /* zero */
    __u32                 nl_pid;     /* port ID */
    __u32                 nl_groups;  /* multicast groups mask */
};
include/linux/netlink.h)

其中
nl_family 填 AF_NETLINK
nl_pad 通常得填 0
nl_pid 代表 netlink 的 unicast address, 如果是 kernel space 就填 0, 如果是 userspace, 在bind的時候就會被填上 user process 的 pid, 但如果 user process 跳過 bind, 那麼就得自行管理這個欄位的值
nl_groups 填這個 socket 所屬的 multicast group


2.2 create netlink socket in kernel

kernel 的 netlink 是在 init 的時候就會呼叫, 不同的group它的init的點也不一樣, 底下是ROUTE的init的點

static int __net_init rtnetlink_net_init(struct net *net)
{
    struct sock *sk;
    sk = netlink_kernel_create(net, NETLINK_ROUTE, RTNLGRP_MAX,
                               rtnetlink_rcv, &rtnl_mutex, THIS_MODULE);
    if (!sk)
        return -ENOMEM;
    net->rtnl = sk;
    return 0;
}
( net/core/rtnetlink.c )

其中 rtnetlink_rcv 是callback function, 負責處理從userspace來的data
NETLINK_ROUTE 是  netlink protocol, 定義在 include/linux/netlink.h
RTNLGRP_MAX 是 route multicast group, 定義在 include/linux/rtnetlink.h

netlink_kernel_create 實作在這裡

netlink_kernel_create(struct net *net, int unit, unsigned int groups,
                      void (*input)(struct sk_buff *skb),
                      struct mutex *cb_mutex, struct module *module)
( net/netlink/af_netlink.c )

netlink protocol 除了 route 之外還有很多, 比如說 kobject 也有自己 init 的點

static int uevent_net_init(struct net *net) {
        struct uevent_sock *ue_sk;

        ue_sk = kzalloc(sizeof(*ue_sk), GFP_KERNEL);
        if (!ue_sk)
                return -ENOMEM;

        ue_sk->sk = netlink_kernel_create(net, NETLINK_KOBJECT_UEVENT,
                                          1, NULL, NULL, THIS_MODULE);
        if (!ue_sk->sk) {
                printk(KERN_ERR
                       "kobject_uevent: unable to create netlink socket!\n");
                kfree(ue_sk);
                return -ENODEV;
        }
        mutex_lock(&uevent_sock_mutex);
        list_add_tail(&ue_sk->list, &uevent_sock_list);
        mutex_unlock(&uevent_sock_mutex);
        return 0;
}
( lib/kobject_uevent.c )

可以看到它的 netlink protocol 是 NETLINK_KOBJECT_UEVENT, 因為 kobject 沒有 multicast group, 所以 group 的欄位填 1, callback填null因為它不需要處理從 userspace 來的 data, mutex 填null 表示使用 default 的 mutex

2.3 maintain netlink socket


許多不同 netlink protocol 的 kernel socket 用 netlink_table 來maintain, 它是個link list
struct netlink_table {
    struct nl_pid_hash hash;
    struct hlist_head mc_list;
    struct listeners __rcu *listeners;
    unsigned int nl_nonroot;
    unsigned int groups;
    struct mutex *cb_mutex;
    struct module *module;
    int registered;
};

要操作這張 table, 有底下幾個 operation

  • nl_table_lock()
  • netlink_lookup()
  • netlink_insert()
比如說, 當 kobject netlink socket 開起來之後, 就會被放進這張 table 裡

2.4 register maintain netlink socket

在開完 socket 之後, 就要註冊在不同的message要什麼callback

rtnetlink_init() {
// ......
    rtnl_register(PF_UNSPEC, RTM_GETLINK, rtnl_getlink, rtnl_dump_ifinfo, rtnl_calcit);
    rtnl_register(PF_UNSPEC, RTM_SETLINK, rtnl_setlink, NULL, NULL);
// ......

rtnl_register 定義如下

rtnl_register(int protocol, int msgtype, // protocol = protocol family, ref include/linux/socket.h
    rtnl_doit_func doit,     // for addition/deletion/modification
    rtnl_dumpit_func dumpit, // for retrieving information
    rtnl_calcit_func calcit) // for calculation of buffer size

從上面例子可以看到 RTM_GETLINK 有填對應的 callback, RTM_SETLINK 有些則填 null

而實際上, rtnl_register 用 array 存對應的callback

static struct rtnl_link *rtnl_msg_handlers[RTNL_FAMILY_MAX + 1];
struct rtnl_link {
    rtnl_doit_func    doit;
    rtnl_dumpit_func  dumpit;
    rtnl_calcit_func  calcit;
};

3. sending rtnetlink message

舉個例子, 當netwrok dev oepn之後, 要通知userspace


3.1 netlink message format

netlink message 前面有 nlmsghdr 可供判斷這段message用的 netlink protocol 是什麼, 接著它的payload 則會依照不同的 netlink protocl 而有不同的format, 但通常都使用TLV的格式


通常一段 netlink message, 可以包含多份 message, 每份 message 前面都有 nlmsghdr, 裡面有一些資訊像是 len 表示這段 message 的長度, type 表示netlink protocol, 也可以利用nlmsghdr和macro幫忙指到對的位置

裡面的 payload, 如果是 route netlink protocol, 它的 format 長這樣

可以看到它的 payload 前面有個 rtmsg 的標頭, 進一步提供 route 相關的 message, 其後接著 TLV 格式的資訊

底下有個例子, 假如收到一包 route 相關 message, 以 buffer 表示其中每段資料的內容


4. Generic Netlink protocol

因為 netlink protocol 只用1 byte來儲存, 所以只有32種 netlink protocol  family 可以用, 如果加新的 netlink protocol family, 很快就不夠用, 再加上每次加上新的, header檔就得重新定義, 所以提出 generic netlink protocol

它的作法就是定義 generic netlink protocol 在 netlink protocol family, 再利用mux來區分

5. libnl.so

在實作上, 拆開每包 netlink message 還是很麻煩, 可以使用 libnl.so
它提供了
  • Connecting/disconnectng of sockets
  • Sending/receiving of data
  • Construction/parsing of messages
  • provides a customizeable receiving state machine
  • provides a abstract data type framework
這些功能對應到這些 library
  • generic netlink family (libnl-genl)
  • routing family (libnl-route)
  • netfilter family (libnl-nf)
底下是libnl的官網架構圖:


底下是它的實作範例



cygwin

notepad++ editor


在 cygwin 底下的vim如果在編輯時, 按上下左右箭頭會出現ASDF, 並且Backspace鍵沒有作用, 那是因為它偵測到雙位元輸入, 這時可以改用

$ vim -N test.txt

在我的環境底下, 這樣做只修好上下左右箭頭, 但Backspace還是壞的

可以考慮用外部的editor像是notepad++, 把下面這段script加到~/.bashrc裡面

#allows you to execute the command npp filename.ext
npp () {
/cygdrive/c/Program\ Files\ \(x86\)/Notepad++/notepad++.exe -multiInst -nosession -noPlugin $(cygpath -w -- "$@")
}

因為我用的是 64bit 電腦, 所以 notepad++.exe 路徑在 c:\Program Files (x86\Notepad++
後面有一些參數像 multiInst表示會額外開新的notepad++視窗而不是開在新的分頁裡
nosession表示不會紀錄開檔紀錄在"open"底下
noPlugin表示把plugin關掉
我是覺得開在新的分頁就好, 所以我的設定是這樣

#allows you to execute the command npp filename.ext
npp () {
/cygdrive/c/Program\ Files\ \(x86\)/Notepad++/notepad++.exe $(cygpath -w -- "$@")
}

最後, 就可以在cygwin底下開notepad了

$ npp textfile1.txt textfile2.txt

但是如果用分頁開有個bug, 就是如果一開始notepad++沒有開著, 那麼command line就會停在那邊等notepad關掉, 所以得先開著notepad++才會讓command line往下走

clear screen

在 linux 可以用 clear 來清光螢幕, cygwin則可以比照上面的方式加上這個功能
在 ~/.bashrc 裡面加上這個

clear() {
printf "\033c"
}

這樣在command line打上clear就可以清光螢幕了

english shell

有時候cygwin安裝的時候, 會變成中聞介面, 常常有亂碼, 要改成英文介面, 在~/.bashrc下面加上這個

export LANG='en_US'



2014/09/11

在blogger顯示程式碼

1. 在"版面配置" -> "新增小工具" -> "HTML/Javascript工具", 標題隨便打

內容填這樣:


<style>
.post .codeblock {
display: block; /* fixes a strange ie margin bug */
font-family: Courier New;
font-size: 10pt;
overflow:auto;
color: #000000
background: #f0f0f0 url(data:image/gif;base64,iVBORw0KGgoAAAANSUhEUgAAAAsAAASwCAYAAAAt7rCDAAAABHNCSVQICAgIfAhkiAAAAQJJREFUeJzt0kEKhDAMBdA4zFmbM+W0upqFOhXrDILwsimFR5pfMrXW5jhZr7PwRlxVX8//jNHrGhExjXzdu9c5IiIz+7iqVmB7Hwp4OMa2nhhwN/PRGEMBh3Zjt6KfpzPztxW9MSAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzB8HS+J9kUTvzEDMwAAAABJRU5ErkJggg==) left top repeat-y;
border: 1px solid #ccc;
padding: 10px 10px 10px 21px;
max-height:1000px;
line-height: 1.2em;
}
</style>

其中color以及background的值可以更改, 請參考RGB三色表

2. 在要顯示成code的html裡, 標籤加上 class='codeblack'
比如說

<pre class="codeblock">I am code block</pre>

3. 如果要印的code有HTML碼, 請先做encode的動作:

http://www.opinionatedgeek.com/DotNet/Tools/HTMLEncode/Encode.aspx

4. 如果要將code上色, 將標籤的class加上prettyprint:

<pre class="codeblock prettyprint"> int main () {
    printf("hello world");
}
</pre>

 顯示效果像這樣
 
int main () {
    printf("hello world");
}

 這樣就大功告成了