2022年4月20日 星期三

使用Edge Impluse build一隻動作分類c/c++ sample

利用Edge Impluse 已有的data set跟training好的model, 產生出c/c++ library, 接著將model include進c/c++ main code, 並帶入一串sensor xyz data來判斷這個動作是左右或上下移動.


0. Windows OS先安裝MinGW

Sourceforge下載來的exe檔常常遇到一個error.








可以直接到這邊抓免安裝包

https://sourceforge.net/projects/mingw-w64/files/mingw-w64/










兩個都可, 下載後unzip到C:\mingw64 , 將C:\mingw64\bin 加到環境變數中.

開cmd測試gcc -v可以正常執行環境就ok.


1. Edge Impluse 專案設定.

使用官方training好的data做範例

https://studio.edgeimpulse.com/public/76063/latest

右上的clone this project, clone到自己的workspace. clone完之後, 選Deployment --> C++ library --> Build

完成之後會自動下載一包my-motion-project-v3.zip


2.  Sample code download

官方sample code
C:

C++:

3. Merge library to sample code

my-motion-project-v3.zip解壓縮後的檔案放到sample code folder中













C++的sample直接build.bat就可以, C的sample要加以下這行在Makefile中

CFLAGS += -DEI_PORTING_MINGW32=1

確認可以正常build完後, 接著帶入要辨識的xyz data. 回到Edge Impluse clone出來的project. Model testing隨意選一筆data show classifiction.
 
進去後會看到連續的raw data, 可以隨意移動框框, 下面的raw features會跟著變化, 都是三個資料為一小組, 直接複製raw features出來.

















C++ sample中, 將資料貼到main.cpp的features array






compile之後run就會看到結果






















C sample使用帶入檔案的方式, 將剛剛copy的raw features另存成一個檔案, 帶入即可























Reference:

2018年4月12日 星期四

Press a key to control a led device on i.mx6

根據前一篇Linux input system and keypad driver on i.mx6

經過上面步驟已經可以正確傳遞keypad event並且透過evdev read function讀到key code. 透過key code可以再control led or pwm device.
透過led.h 中的led_trigger_register_simple以及led_trigger_event便可以trigger event set led brightness.
 
Sample:
evdev.c

#include 
struct led_trigger *test_led_trigger = 0;

static int __init evdev_init(void)
{
    led_trigger_register_simple("debug-leds", &test_led_trigger); //註冊test_led_trigger的trigger led label是debug-leds

    return input_register_handler(&evdev_handler);
}

static void __exit evdev_exit(void)
{
    led_trigger_unregister_simple(test_led_trigger);
    test_led_trigger = 0;
 
    input_unregister_handler(&evdev_handler);
}

在device tree中宣告
    leds {
        debug-leds {
            gpios = <&gpio1 2 0>;
            linux,default-trigger = "debug-leds";
            default-state = "off";
        };
};

接著回到evdev.c
static ssize_t evdev_read(struct file *file, char __user *buffer,
     size_t count, loff_t *ppos)
{
…
//此處會讀input event的data
while (read + input_event_size() <= count &&
       evdev_fetch_next_event(client, &event)) {
 printk("event type = %x, code = %x, value = %x\n",event.type, event.code, event.value);
            if(event.code == 0x1c){ //key code is "enter"
                printk("key code is 0x1c\n");
                led_trigger_event(test_led_trigger, LED_FULL); //設定test_led_trigger control 的led 亮度為FULL(255)
            }
 read += input_event_size();
}
…
}

如此在收到特定key code時, "gpio1 2"這顆led便會被開到全亮.
這邊要注意的是若要控制的是pwm device, 那需要先將device宣告為pwm-leds就一樣可以使用led trigger來控制. 只需在led_trigger_event第二個arguments填入0-255的數字就可以設定pwm cycle.
 
device tree example:
pwmleds {
    compatible = "pwm-leds";
    backlightk {
        label = "backlight-leds";
        pwms = <&pwm1 0 8333333>;
        linux,default-trigger = "debug-leds";
        max-brightness = <255>;
    };
};

詳細code流程如下
 
led_triggers.c
void led_trigger_register_simple(const char *name, struct led_trigger **tp)
{
 struct led_trigger *trig;
 int err;

 trig = kzalloc(sizeof(struct led_trigger), GFP_KERNEL);

 if (trig) {
  trig->name = name;
  err = led_trigger_register(trig); //將led_trigger建立到list中
  if (err < 0) {
   kfree(trig);
…
}

Trigger event時
void led_trigger_event(struct led_trigger *trig,
   enum led_brightness brightness)
{
 struct list_head *entry;

 if (!trig)
  return;

 read_lock(&trig->leddev_list_lock);
 list_for_each(entry, &trig->led_cdevs) { //search list to find the trigger
  struct led_classdev *led_cdev;

  led_cdev = list_entry(entry, struct led_classdev, trig_list);
  led_set_brightness(led_cdev, brightness); //找到device之後最終調用device driver的brightness_set做亮度設定
 }
 read_unlock(&trig->leddev_list_lock);
}

Linux input system and keypad driver on i.mx6

Refer to below, register gpio keypad to a  gpio-keys
https://community.nxp.com/thread/343346

Declare keys in device tree
 
keys{
        compatible = "gpio-keys";
        #address-cells = <1>;
        #size-cells = <0>;
        autorepeat;
        keypad-01{
            label = "keypad-01";
            gpios = <&gpio4 30 1>;
            linux,code = <2>; /* KEY_RESERVED  */
        };
        keypad-02{
            label = "keypad-02";
            gpios = <&gpio4 31 1>;
            linux,code = <3>; /* KEY_RESERVED  */
        };
};   
Note: 若設置autorepeat那按下key不放時, input event會一直發. 若沒設定時就只會發一次, 但是Yocto OS在沒收到release event之前會自動做autorepeat的動作.

宣告為"gpio-keys" 或是 "gpio-keys-polled" keypad都可以正常動作. 差異在"gpio-keys-polled"需多定義"poll-interval". 還有在register input device的時候一個是register成input device, 另一個是register成input polled device. 資料結構不同. 以及一個是使用interupt方式, 另一個則是polling mode.


根據以下兩張圖, linux input system 主要分為三個部分, input core, input device driver, input event driver. Input device driver負責control各interface的hardware device. Input event driver負責處理傳入的事件以及向上傳到application. Input core負責串起device driver以及event driver.





Event driver (Ex: evdev.c) init時, 會註冊一個input handler到input system list.

 
static int __init evdev_init(void)
{
	return input_register_handler(&evdev_handler);
}

Device probe時, 會註冊一個device到input system list.

 
error = input_register_polled_device(poll_dev);
if (error) {
	dev_err(dev, "unable to register polled device, err=%d\n",
		error);
	goto err_free_gpio;
}
當device以及handler做register時, input core都會使用input_attach_handler 來搜尋相對應的device以及handler. 若是有找到相對應的device以及handler, 則建出一個input handle供以後使用.
當有找到相對應的device以及handler時, 會調用handler的connect function, 以evdev來說, 就會在connect function當中建出/dev/input/eventX
便可以將event與device driver串在一起.

 
input.c
int input_register_handler(struct input_handler *handler)
{
	struct input_dev *dev;
	int error;

	error = mutex_lock_interruptible(&input_mutex);
	if (error)
		return error;

	INIT_LIST_HEAD(&handler->h_list);

	list_add_tail(&handler->node, &input_handler_list);

	list_for_each_entry(dev, &input_dev_list, node)
		input_attach_handler(dev, handler);

	input_wakeup_procfs_readers();

	mutex_unlock(&input_mutex);
	return 0;
}

int input_register_device(struct input_dev *dev)
{
…
	list_add_tail(&dev->node, &input_dev_list);

	list_for_each_entry(handler, &input_handler_list, node)
		input_attach_handler(dev, handler);

	input_wakeup_procfs_readers();

	mutex_unlock(&input_mutex);
…
}

static int input_attach_handler(struct input_dev *dev, struct input_handler *handler)
{
	const struct input_device_id *id;
	int error;

	id = input_match_device(handler, dev);
	if (!id)
		return -ENODEV;

	error = handler->connect(handler, dev, id); //匹配成功就调用handler的connect函数 
	if (error && error != -ENODEV)
		pr_err("failed to attach handler %s to device %s, error: %d\n",
		       handler->name, kobject_name(&dev->dev.kobj), error);

	return error;
}

evdev.c
static int evdev_connect(struct input_handler *handler, struct input_dev *dev,
			 const struct input_device_id *id)
{
…
	dev_set_name(&evdev->dev, "event%d", dev_no);

	evdev->handle.dev = input_get_device(dev);
	evdev->handle.name = dev_name(&evdev->dev);
	evdev->handle.handler = handler;
	evdev->handle.private = evdev;
	
	evdev->dev.devt = MKDEV(INPUT_MAJOR, minor);
	evdev->dev.class = &input_class;
	evdev->dev.parent = &dev->dev;
	evdev->dev.release = evdev_free;
	device_initialize(&evdev->dev);

	error = input_register_handle(&evdev->handle);
	if (error)
		goto err_free_evdev;

	cdev_init(&evdev->cdev, &evdev_fops);
…
}
到這邊串起之後, 便可以將input device產生的訊息(touch screen座標, key-code…etc)傳給input handler. 或是將input handler的訊息交給input device(如鼠标的闪灯命令).

實際在使用時, 如果按下一個key, 不管是interupt或是polling mode, 都是在收到動作之後, 透過input_event來向input handler傳遞type, code, value這三項內容. 然後便可以從cat /dev/input/eventX 或是調用evdev的read function來得到輸入的資料.

open /dev/input/eventX 之後便可使用evdev_fops的read/write/ioctl function, Yocto在進入os時有open device, 並且read device. 但是還沒找到Yocto在哪邊做open及read.

到這邊就可以完成按下gpio keypad發出一個linux standard keycode.


Reference:
https://blog.csdn.net/yueqian_scut/article/details/47903853
https://blog.csdn.net/yueqian_scut/article/details/48026955
https://blog.csdn.net/qq_695538007/article/details/9153099
https://blog.csdn.net/qq_695538007/article/details/9153135
http://www.jollen.org/blog/2009/04/linux_input_device_apis.html
https://patchwork.kernel.org/patch/2848259/
https://community.nxp.com/thread/343346

2017年12月11日 星期一

i.mx6 LVDS clock structure

Dump all clock source data command
#cat /sys/kernel/debug/clk/clk_summary



LDB clock use path LDB_DI1_IPU -->ldb_di1_ipu_div -->ldb_di1_clk_sel
從ldb_di1_clk_sel過來的clock固定只能 /3.5(lvds split mode) or /7(lvds non-split mode)



在ldb_di1_clk_sel則可以選擇5個不同clock source
其中PLL2 PFD0/PLL2 PFD2/pll3_sw_clk往前都沒有其他的clock select或是divider.
mmdc_ch1 clock如下圖則還有clock select以及divider
黃色標出的部分都可以再尋找register做clock source以及divider的設定


pll5 clock set
pll5 clock可分為兩個部分.
1. 從24MHz OSC出來計算PLL5的clock, 參考公式如下圖

Fref = 24Mhz
DIV_SELECT = CCM_ANALOG_PLL_VIDEOn offset 0-6, range 27-54
NUM = CCM_ANALOG_PLL_AUDIO_NUM offset 0-29
DENOM = CCM_ANALOG_PLL_AUDIO_DENOM offset 0-29
NUM must less than DENOM.
Example
24MHz * (29 + 8666666 /10000000 ) = 716.79984MHz
716MHz = pll5 = pll5_bypass = pll5_video

2. 由第一步計算出的PLL5 clock, 再經由2個2bit divider, 可以 /1 /2 /4 /8 /16來產生出最終的pll5_main_clk


總結:
由ldb_di1_clk_sel當中選擇出的clock, 再根據split mode來決定/3.5 or /7就可算出最終ldb輸出的clock frequency.

2017年9月20日 星期三

mtd device for spi nor flash

spi flash不是直接access到spi flash, 而是透過mtd再往下

                   MTD
         ------------------------
              SPI NOR framework
         ------------------------
                  m25p80
         ------------------------
      SPI bus driver
         ------------------------
      SPI NOR chip

在i.mx cpu下的架構會略有不同

                   MTD
         ------------------------
              SPI NOR framework
         ------------------------
                fsl-quadSPI
         ------------------------
      SPI NOR chip

但是從log來看fsl-quadSPI還是有用到m25p80這隻driver

m25p80目前看來可以通用在所有的spi nor flash, 要新增只要把id相關瑱入table中就好

目前spi nor flash不透過spidev這個device來access, 所以不需要掛spidev在spi device下.

spidev目前看來是直接access spi device的, 如果有需要透過spidev直接control device才需加入device tree

http://blog.5ibc.net/p/14266.html

2017年9月8日 星期五

git branch have diverged

============ 當 Conflict when git pull =====================

如果 git pull 時發現有 Conflict, 但你又明明沒有加入任何 commit,
有可能是因為原來的 branch 已經走到分支出去了,
你可以打入 git status

# On branch master
# Your branch and 'origin/master' have diverged,
# and have 32 and 83 different commit(s) each, respectively.
#

第一個數字 32 代表著你比 origin/master 多了 32 個 patch, 第二個數字 83 代表著你比 origin/master 少了 83 個 patch 如果你很有興趣知道是那一些可以利用 git cherry origin/master 來看

1. 執行 git reset --hard HEAD^ 一直到你可以做 fast-forwarded

2. 直接執行 git pull --rebase origin/master

http://pjack1981.blogspot.tw/2012/03/git.html
https://github.com/zlargon/git-tutorial/blob/master/remote/sync.md

2017年7月24日 星期一

Android propery

Android property 與BIOS的variable感覺相當類似, 都是可以作為一些變數往後傳遞.
但是property更簡單的是因為他是用檔案形式, 所以要驗證的時候開檔案來看就好.

property service是在init.c當中啟動.
1)间接调用__system_property_area_init():打开属性共享内存,并记入__system_property_area变量;
2)间接调用init_workspace():只读打开属性共享内存,并记入环境变量;
3)根据init.rc,异步激发property_service_init_action(),该函数中会:
    l  加载若干属性文本文件,将具体属性、属性值记入属性共享内存;
    l  创建并监听socket;
4)根据init.rc,异步激发queue_property_triggers_action(),将刚刚加载的属性对应的激发动作,推入action列表。






































接著則是在init.rc中會看到一段
on load_all_props_action
    load_all_props
這邊其實才會真正的load file system中的properties
從keywords.h中可以看到
    KEYWORD(load_all_props,        COMMAND, 0, do_load_all_props)
所以去看在do_load_all_props在property_service.c中
    load_properties_from_file(PROP_PATH_SYSTEM_BUILD, NULL);
    load_properties_from_file(PROP_PATH_SYSTEM_DEFAULT, NULL);
    load_properties_from_file(PROP_PATH_VENDOR_BUILD, NULL);
    load_properties_from_file(PROP_PATH_FACTORY, "ro.*");
這邊就會實際的去抓file system中的property files.
有個需注意的部份是在這邊的property_set中會將ro.開頭的property set都擋掉
if(!strncmp(name, "ro.", 3)) return -1;
所以若是在跑到load_all_props之前都還可以set ro.開頭的property, 但是在load_all_props之後ro.xxx就真的變成read only的property了.

參考資料: