0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

fireflyCORE-PX30-JD4驅(qū)動開發(fā)介紹

firefly ? 來源:firefly ? 作者:firefly ? 2019-12-19 16:40 ? 次閱讀
驅(qū)動開發(fā)
ADC 使用
簡介

AIO-PX30JD4 開發(fā)板上的 AD 接口有兩種,分別為:溫度傳感器 (Temperature Sensor)、逐次逼近ADC (Successive Approximation Register)。其中:

  • TS-ADC(Temperature Sensor):支持兩通道,時鐘頻率必須低于800KHZ

  • SAR-ADC(Successive Approximation Register):支持三通道單端10位的SAR-ADC,時鐘頻率必須小于13MHZ。

Linux內(nèi)核采用工業(yè) I/O 子系統(tǒng)(iio子系統(tǒng))來控制 ADC,該子系統(tǒng)主要為 AD 轉(zhuǎn)換或者 DA 轉(zhuǎn)換而設(shè)計。本文以配置SAR-ADC為例,主要介紹 SAR-ADC的基本配置和使用的方法,其相關(guān)的數(shù)據(jù)結(jié)構(gòu),源碼路徑以及配置步驟如下:

數(shù)據(jù)結(jié)構(gòu)

iio_channel結(jié)構(gòu)體

structiio_channel{structiio_dev*indio_dev;//工業(yè)I/O設(shè)備conststructiio_chan_spec*channel;//I/O通道void*data;};

iio_dev結(jié)構(gòu)體

該結(jié)構(gòu)體主要是用于描述IO口所屬的設(shè)備,其具體定義如下:

structiio_dev{intid;intmodes;intcurrentmode;structdevicedev;structiio_event_interface*event_interface;structiio_buffer*buffer;structlist_headbuffer_list;intscan_bytes;structmutexmlock;constunsignedlong*available_scan_masks;unsignedmasklength;constunsignedlong*active_scan_mask;boolscan_timestamp;unsignedscan_index_timestamp;structiio_trigger*trig;structiio_poll_func*pollfunc;structiio_chan_specconst*channels;intnum_channels;structlist_headchannel_attr_list;structattribute_groupchan_attr_group;constchar*name;conststructiio_info*info;structmutexinfo_exist_lock;conststructiio_buffer_setup_ops*setup_ops;structcdevchrdev;#define IIO_MAX_GROUPS 6conststructattribute_group*groups[IIO_MAX_GROUPS+1];intgroupcounter;unsignedlongflags;#if defined(CONFIG_DEBUG_FS)structdentry*debugfs_dentry;unsignedcached_reg_addr;#endif};

iio_chan_spec結(jié)構(gòu)體

該結(jié)構(gòu)體主要用于描述單個通道的屬性,具體定義如下:

structiio_chan_spec{enumiio_chan_typetype;//描述通道類型intchannel;//通道號intchannel2;//通道號unsignedlongaddress;//通道地址intscan_index;struct{charsign;u8realbits;u8storagebits;u8shift;enumiio_endianendianness;}scan_type;longinfo_mask;longinfo_mask_separate;longinfo_mask_shared_by_type;longevent_mask;conststructiio_chan_spec_ext_info*ext_info;constchar*extend_name;constchar*datasheet_name;unsignedmodified:1;unsignedindexed:1;unsignedoutput:1;unsigneddifferential:1;};
源碼路徑
* /kernel/drivers/iio/adc/rockchip_saradc.c 此驅(qū)動通過解析內(nèi)核設(shè)備樹中的saradc資源,申請使用iio子系統(tǒng)來控制saradc。 * kernel/drivers/input/keyboard/adc-keys.c 此驅(qū)動通過判斷ADC通道的值范圍,來判斷哪個按鍵按下了。
配置步驟

配置DTS節(jié)點

第一步:在AIO-PX30JD4的 DTS 文件:kernel/arch/arm64/boot/dts/rockchip/px30.dtsi中,添加saradc資源,應(yīng)如下:

saradc:saradc@ff288000{compatible="rockchip,px30-saradc","rockchip,rk3399-saradc";reg=<0x00xff2880000x00x100>;interrupts=;#io-channel-cells = <1>;clocks=<&cruSCLK_SARADC>,<&cruPCLK_SARADC>;clock-names="saradc","apb_pclk";resets=<&cruSRST_SARADC_P>;reset-names="saradc-apb";status="disabled";};

第二步:根據(jù)用戶的通道需要選擇對應(yīng)的saradc通道,本次例程使用AIO-PX30-JD4上的ADC按鍵檢測,選擇通道2,配置如下:

adc-keys{compatible="adc-keys";io-channels=<&saradc2>;io-channel-names="buttons";poll-interval=<100>;keyup-threshold-microvolt=<1800000>;vol-up-key{linux,code=;label="volume up";press-threshold-microvolt=<17000>;};};
  • io-channels 屬性 為 選擇的通道號,這里選擇通道2

  • io-channel-names 屬性 表示 為申請的通道起一個別名。

  • keyup-threshold-microvolt 屬性 表示按鍵抬起,saradc通道2的電壓(單位微伏)。

  • press-threshold-microvolt 屬性 表示按鍵按下,saradc通道2的電壓。

  • vol-up-key 在硬件連接上,為AIO-PX30-JD4 上的recovery 按鍵。

  • linux,code 屬性 為 按鍵上報的鍵值,鍵值對應(yīng)的動作 為 “音量+” 。

在saradc驅(qū)動文件中匹配 saradc 的dts 節(jié)點

第一步: 在內(nèi)核設(shè)備樹添加saradc資源之后,可以在源碼kernel/drivers/iio/adc/rockchip_saradc.c中添加對應(yīng)的saradc數(shù)據(jù)結(jié)構(gòu)體

staticconststructrockchip_saradc_datapx30_saradc_data={.num_bits=10,.channels=rockchip_px30_saradc_iio_channels,.num_channels=ARRAY_SIZE(rockchip_px30_saradc_iio_channels),.clk_rate=1000000,};
staticconststructof_device_idrockchip_saradc_match[]={{.compatible="rockchip,saradc",.data=&saradc_data,},{.compatible="rockchip,px30-saradc",.data=&px30_saradc_data,},{},};

第二步: 在rockchip_saradc_match[] 中,添加px30的compatible屬性,使得saradc驅(qū)動可以匹配到saradc設(shè)備。因如下:

static const struct of_device_id rockchip_saradc_match[] = { ...... { .compatible = "rockchip,px30-saradc", //用于匹配px30的saradc設(shè)備 .data = &px30_saradc_data, //px30的saradc相關(guān)屬性。 }, {}, ...... };

第三步: 填充saradc的platform_driver結(jié)構(gòu)體:

staticstructplatform_driverrockchip_saradc_driver={.probe=rockchip_saradc_probe,.remove=rockchip_saradc_remove,.driver={.name="rockchip-saradc",.of_match_table=rockchip_saradc_match,.pm=&rockchip_saradc_pm_ops,},};

第四步:通過module_platform_driver(rockchip_saradc_driver)宏平進行驅(qū)動的注冊。

在設(shè)備上電的時候,內(nèi)核會解析內(nèi)核設(shè)備樹,當(dāng)檢測到設(shè)備樹上saradc的compatible屬性與saradc驅(qū)動的of_device_id中的compatible成員一致的時候,便會調(diào)用rockchip_saradc.c中的rockchip_saradc_probe()函數(shù)來進行iio系統(tǒng)的adc設(shè)備的資源申請以及初始化(此處不再贅述,用戶可自行查看源碼)。

在進入系統(tǒng)后,會出現(xiàn)一個 /sys/bus/iio/devices/iio:device0的目錄,表示創(chuàng)建成功。

在adc-keys.c驅(qū)動文件中匹配 adc-keys的dts 節(jié)點

第一步: 填充ADC按鍵驅(qū)動的adc_keys_of_match[]中的compatible成員用于匹配設(shè)備

staticconststructof_device_idadc_keys_of_match[]={{.compatible="adc-keys",},{}};

第二步: 填充驅(qū)動結(jié)構(gòu)體

staticstructplatform_driver__refdataadc_keys_driver={.driver={.name="adc_keys",.of_match_table=of_match_ptr(adc_keys_of_match),},.probe=adc_keys_probe,};

第三步: 使用module_platform_driver(adc_keys_driver);往內(nèi)核注冊該驅(qū)動。

第四步: 設(shè)備樹 compatible匹配正確,驅(qū)動注冊成功之后,便會調(diào)用ADC按鍵驅(qū)動的adc_keys_probe()函數(shù),進行輸入子系統(tǒng)設(shè)備的注冊(因為是按鍵驅(qū)動,所以使用輸入子系統(tǒng),此部分不在此講述)與saradc的io通道的申請。

static int adc_keys_probe(struct platform_device *pdev) { // 1.Initialize the device, get IO resources, apply for IO channel. struct device *dev = &pdev->dev; struct adc_keys_state *st; struct input_polled_dev *poll_dev; struct input_dev *input; enum iio_chan_type type; int i, value; int error; st = devm_kzalloc(dev, sizeof(*st), GFP_KERNEL); if (!st) return -ENOMEM; st->channel = devm_iio_channel_get(dev, "buttons"); if (IS_ERR(st->channel)) return PTR_ERR(st->channel); if (!st->channel->indio_dev) return -ENXIO; error = iio_get_channel_type(st->channel, &type); if (error < 0) return error; if (type != IIO_VOLTAGE) { dev_err(dev, "Incompatible channel type %d\n", type); return -EINVAL; } // 2.Get information from the device tree and save the voltage of each key if (device_property_read_u32(dev, "keyup-threshold-microvolt", &st->keyup_voltage)) { dev_err(dev, "Invalid or missing keyup voltage\n"); return -EINVAL; } //3.Input subsystem device registration ...... //4.Conduct polling detection of input subsystem devices ...... }

在進入系統(tǒng)后,通過 getevent 命令 :

adddevice1:/dev/input/event0name:"rk8xx_pwrkey"adddevice2:/dev/input/event3name:"adc-keys"adddevice3:/dev/input/event1name:"gslX680"adddevice4:/dev/input/event2name:"rk_headset"

其中我們可以看到:

adddevice2:/dev/input/event3name:"adc-keys"

這樣表示我們的設(shè)備已經(jīng)創(chuàng)建成功。

按鍵檢測

adc-keys.c驅(qū)動是通過輸入子系統(tǒng)的輪詢檢測函數(shù)adc_keys_poll(),來不斷地讀取saradc通道的值,當(dāng)不同按鍵按下的時候,是有不同的電壓值的:

staticvoidadc_keys_poll(structinput_polled_dev*dev){structadc_keys_state*st=dev->private;inti,value,ret;u32diff,closest=0xffffffff;intkeycode=0;ret=iio_read_channel_processed(st->channel,&value);if(unlikely(ret<0)){/*Forciblyreleasekeyifanywaspressed*/value=st->keyup_voltage;}else{for(i=0;inum_keys;i++){diff=abs(st->map[i].voltage-value);if(diffmap[i].keycode;}}}if(abs(st->keyup_voltage-value)last_key&&st->last_key!=keycode)input_report_key(dev->input,st->last_key,0);if(keycode)input_report_key(dev->input,keycode,1);input_sync(dev->input);st->last_key=keycode;}

所以當(dāng)recovery按鍵按下的時候,通過iio_read_channel_processed()函數(shù)獲取到的電壓值如果與設(shè)備樹配置相符合的話,就會觸發(fā)按鍵上報事件,而用戶層會收到事件,從屏幕可以看到有 ”音量+“ 的動作。

獲取所有ADC值

有個便捷的方法可以在命令行中直接查詢到每個SARADC的值:

cat/sys/bus/iio/devices/iio\:device0/in_voltage*_raw

其中in_voltage0_raw為通道0,in_voltage01_raw為通道1,以此類推。

以上面的例子為例,命令行輸入 cat /sys/bus/iio/devices/iio:device0/in_voltage2_raw 來獲取ADC電壓轉(zhuǎn)換后的數(shù)字量

會打印 : 0; // 表示recovery 按鍵按下,對應(yīng) 0.17V模擬量 當(dāng)無動作的時候 會打印 :1020; //表示按鍵抬起,對應(yīng) 1.8V 模擬量
iio操作說明

獲取 AD 通道

structiio_channel*chan;//定義IIO通道結(jié)構(gòu)體chan=iio_channel_get(&pdev->dev,xxx);//獲取IIO通道結(jié)構(gòu)體

注:iio_channel_get 通過 probe 函數(shù)傳進來的參數(shù) pdev 獲取 IIO 通道結(jié)構(gòu)體,probe 函數(shù)如下:

讀取 AD 采集到的原始數(shù)據(jù)

intval,ret;ret=iio_read_channel_raw(chan,&val);

調(diào)用 iio_read_channel_raw 函數(shù)讀取 AD 采集的原始數(shù)據(jù)并存入 val 中。

計算采集到的電壓

使用標(biāo)準(zhǔn)電壓將 AD 轉(zhuǎn)換的值轉(zhuǎn)換為用戶所需要的電壓值。其計算公式如下:

Vref/(2^n-1)=Vresult/raw

注:

  • Vref 為標(biāo)準(zhǔn)電壓

  • n 為 AD 轉(zhuǎn)換的位數(shù)

  • Vresult 為用戶所需要的采集電壓

  • raw 為 AD 采集的原始數(shù)據(jù)

例如,標(biāo)準(zhǔn)電壓為 1.8V,AD 采集位數(shù)為 10 位,AD 采集到的原始數(shù)據(jù)為 568,則:

Vresult=(1800mv*568)/1023;
iio接口說明
structiio_channel*iio_channel_get(structdevice*dev,constchar*consumer_channel);
  • 功能:獲取 iio 通道描述

  • 參數(shù):

    • dev: 使用該通道的設(shè)備描述指針

    • consumer_channel: 該設(shè)備所使用的 IIO 通道描述指針

voidiio_channel_release(structiio_channel*chan);
  • 功能:釋放 iio_channel_get 函數(shù)獲取到的通道

  • 參數(shù):

    • chan:要被釋放的通道描述指針

intiio_read_channel_raw(structiio_channel*chan,int*val);
  • 功能:讀取 chan 通道 AD 采集的原始數(shù)據(jù)。

  • 參數(shù):

    • chan:要讀取的采集通道指針

    • val:存放讀取結(jié)果的指針

FAQs

為何按上面的步驟申請SARADC,會出現(xiàn)申請報錯的情況?

驅(qū)動需要獲取ADC通道來使用時,需要對驅(qū)動的加載時間進行控制,必須要在saradc初始化之后。saradc是使用module_platform_driver()進行平臺設(shè)備驅(qū)動注冊,最終調(diào)用的是module_init()。所以用戶的驅(qū)動加載函數(shù)只需使用比module_init()優(yōu)先級低的,例如:late_initcall(),就能保證驅(qū)動的加載的時間比saradc初始化時間晚,可避免出錯。

GPIO 使用
簡介

GPIO, 全稱 General-Purpose Input/Output(通用輸入輸出),是一種軟件運行期間能夠動態(tài)配置和控制的通用引腳。 PX30有4組GPIO bank:GPIO0~GPIO3,每組又以 A0~A7, B0~B7, C0~C7, D0~D7 作為編號區(qū)分(GPIO0在PD_PMU子系統(tǒng)中,GPIO1/GPIO2/GPIO3在PD_BUS子系統(tǒng)中)。 所有的GPIO在上電后的初始狀態(tài)都是輸入模式,可以通過軟件設(shè)為上拉或下拉,也可以設(shè)置為中斷腳,驅(qū)動強度都是可編程的。每個 GPIO 口除了通用輸入輸出功能外,還可能有其它復(fù)用功能,例如:

  • GPIO0_C2 可復(fù)用為 I2C1_SCL端口

  • GPIO0_C3 可復(fù)用為 I2C1_SDA端口

每個 GPIO 口的驅(qū)動電流、上下拉和重置后的初始狀態(tài)都不盡相同,詳細(xì)情況請參考《px30 規(guī)格書》中的 “Chapter 21 GPIO” 一章。 px30 的 GPIO 驅(qū)動是在以下 pinctrl 文件中實現(xiàn)的:

kernel/drivers/pinctrl/pinctrl-rockchip.c

其核心是填充 GPIO bank 的方法和參數(shù),并調(diào)用 gpiochip_add 注冊到內(nèi)核中。

例子

DTS配置

本文以px30的gslx680外設(shè)(基于i2c通信的觸摸屏)為例,講述 gpio的輸入輸出,中斷,復(fù)用功能的使用,該驅(qū)動源碼在SDK的路徑為:

kernel/drivers/input/touchscreen/gslx680_firefly.c

以下就以該驅(qū)動為例介紹GPIO的操作。

本例子所需添加的DTS資源如下所示:

kernel/arch/arm64/boot/dts/rockchip/px30-firefly-demo.dtsigslx680:gslx680@41{compatible="gslX680";reg=<0x41>;screen_max_x=<800>;screen_max_y=<1280>;touch-gpio=<&gpio05IRQ_TYPE_LEVEL_LOW>;reset-gpio=<&gpio012GPIO_ACTIVE_HIGH>;flip-x=<1>;flip-y=<0>;swap-xy=<0>;gsl,fw=<1>;};
kernel/arch/arm64/boot/dts/rockchip/px30.dtsii2c1:i2c@ff190000{compatible="rockchip,rk3399-i2c";reg=<0x00xff1900000x00x1000>;clocks=<&cruSCLK_I2C1>,<&cruPCLK_I2C1>;clock-names="i2c","pclk";interrupts=;pinctrl-names="default","gpio"(此gpio字段源碼未添加);pinctrl-0=<&i2c1_xfer>;pinctrl-1=<&i2c1_gpio>;//此處源碼未添加#address-cells = <1>;#size-cells = <0>;status="disabled";};

輸入輸出引腳配置

這里使用的是gslx680外設(shè)的reset(復(fù)位)引腳來講述GPIO的輸入輸出操作。 在DTS配置如下資源:

reset-gpio=<&gpio012GPIO_ACTIVE_HIGH>;

AIO-PX30JD4的dts對引腳的描述與Firefly-RK3288有所區(qū)別,GPIO0_B4被描述為:<&gpio0 12 GPIO_ACTIVE_HIGH>,這里的12來源于:8+4=12,其中8是因為GPIO0_B4是屬于GPIO0的B組,如果是A組的話則為0,如果是C組則為16,如果是D組則為24,以此遞推,而4是因為B4后面的4。GPIO_ACTIVE_HIGH表示高電平有效,如果想要低電平有效,可以改為:GPIO_ACTIVE_LOW,這個屬性將被驅(qū)動所讀取。

中斷引腳配置

這里使用的是gslx680外設(shè)的irq(中斷) 引腳來講述GPIO的中斷功能 在DTS中配置如下資源:

touch-gpio=<&gpio05IRQ_TYPE_LEVEL_LOW>;

其中 gpio0 5 的意思是使用gpio0_A5 為中斷引腳,IRQ_TYPE_LEVEL_LOW意思是該引腳低電平(下降沿)的時候觸發(fā)中斷,跳到中斷函數(shù)執(zhí)行,中斷的觸發(fā)類型還可以配置如下:

IRQ_TYPE_NONE //默認(rèn)值,無定義中斷觸發(fā)類型 IRQ_TYPE_EDGE_RISING //上升沿觸發(fā) IRQ_TYPE_EDGE_FALLING //下降沿觸發(fā) IRQ_TYPE_EDGE_BOTH //上升沿和下降沿都觸發(fā) IRQ_TYPE_LEVEL_HIGH //高電平觸發(fā) IRQ_TYPE_LEVEL_LOW //低電平觸發(fā)

復(fù)用功能引腳配置

查看芯片的數(shù)據(jù)手冊,可以知道:

Pad# func0 func1I2C1_SDA/GPIO0_C2gpio0c2i2c1_sclI2C1_SCL/GPIO0_C3gpio0c3i2c1_sda

在上面i2c1的dts的配置中,主要有以下關(guān)鍵的描述

  • pinctrl-names 定義了狀態(tài)名稱列表: default (i2c 功能) 和 gpio 兩種狀態(tài)。

  • pinctrl-0 定義了狀態(tài) 0 (即 default)時需要設(shè)置的 pinctrl: &i2c4_xfer

  • pinctrl-1 定義了狀態(tài) 1 (即 gpio)時需要設(shè)置的 pinctrl: &i2c4_gpio

由于在i2c1的dts上gpio的字段屬性沒有添加,所以默認(rèn)該兩個引腳設(shè)置為i2c復(fù)用功能,其中pinctrl的描述可以在kernel/arch/arm64/boot/dts/rockchip/px30.dtsi 找到 :

pinctrl:pinctrl{compatible="rockchip,px30-pinctrl";rockchip,grf=<&grf>;rockchip,pmu=<&pmugrf>;#address-cells = <2>;#size-cells = <2>;ranges;............i2c1{i2c1_xfer:i2c1-xfer{rockchip,pins=<0RK_PC2RK_FUNC_1&pcfg_pull_none_smt>,<0RK_PC3RK_FUNC_1&pcfg_pull_none_smt>;};/*此段源碼未添加i2c1-gpio:i2c1-gpio{rockchip,pins=<0RK_PC2RK_FUNC_GPIO&pcfg_pull_none>,<0RK_PC3RK_FUNC_GPIO&pcfg_pull_none>;};*/};......}

其中 0 RK_PC2 表示的是GPIO0_C2引腳,0 RK_PC3 表示的是 GPIO0_C3引腳

RK_FUNC_1,RK_FUNC_GPIO的定義在 kernel/include/dt-bindings/pinctrl/rockchip.h 中可以找到:

#define RK_FUNC_GPIO 0#define RK_FUNC_1 1#define RK_FUNC_2 2#define RK_FUNC_3 3#define RK_FUNC_4 4#define RK_FUNC_5 5#define RK_FUNC_6 6#define RK_FUNC_7 7

在復(fù)用時,如果選擇了 “default” (即 i2c 功能),系統(tǒng)會應(yīng)用 i2c1_xfer 這個 pinctrl,最終將 GPIO0_C2 和 GPIO0_C3 兩個針腳切換成對應(yīng)的 i2c 功能;而如果選擇了 “gpio” ,系統(tǒng)會應(yīng)用 i2c1_gpio 這個 pinctrl,將 GPIO0_C2 和 GPIO0_C3 兩個針腳還原為 GPIO 功能。

由于px30的i2c都是默認(rèn)復(fù)用的,所以在源SDK的px30.dtsi中并沒有加上gpio的選擇,所以,在i2c總線驅(qū)動中:kernel/drivers/i2c/busses/i2c-rk3x.c,并沒有加上切換復(fù)用功能的源碼

如需了解i2c總線驅(qū)動是如何切換復(fù)用功能的,可以參考源碼SDK中的rockchip的官方例子:kernel/drivers/i2c/busses/i2c-rockchip.c 中的rockchip_i2c_probe()函數(shù)。

static int rockchip_i2c_probe(struct platform_device *pdev){ struct rockchip_i2c *i2c = NULL; struct resource *res; struct device_node *np = pdev->dev.of_node; int ret; // ... i2c->sda_gpio = of_get_gpio(np, 0); if (!gpio_is_valid(i2c->sda_gpio)) { dev_err(&pdev->dev, "sda gpio is invalid\n"); return -EINVAL; } ret = devm_gpio_request(&pdev->dev, i2c->sda_gpio, dev_name(&i2c->adap.dev)); if (ret) { dev_err(&pdev->dev, "failed to request sda gpio\n"); return ret; } i2c->scl_gpio = of_get_gpio(np, 1); if (!gpio_is_valid(i2c->scl_gpio)) { dev_err(&pdev->dev, "scl gpio is invalid\n"); return -EINVAL; } ret = devm_gpio_request(&pdev->dev, i2c->scl_gpio, dev_name(&i2c->adap.dev)); if (ret) { dev_err(&pdev->dev, "failed to request scl gpio\n"); return ret; } i2c->gpio_state = pinctrl_lookup_state(i2c->dev->pins->p, "gpio"); if (IS_ERR(i2c->gpio_state)) { dev_err(&pdev->dev, "no gpio pinctrl state\n"); return PTR_ERR(i2c->gpio_state); } pinctrl_select_state(i2c->dev->pins->p, i2c->gpio_state); gpio_direction_input(i2c->sda_gpio); gpio_direction_input(i2c->scl_gpio); pinctrl_select_state(i2c->dev->pins->p, i2c->dev->pins->default_state); // ... }

首先是調(diào)用 of_get_gpio 取出設(shè)備樹中 i2c1 結(jié)點的 gpios 屬于所定義的兩個 gpio:

gpios=<&gpio0GPIO_C2GPIO_ACTIVE_LOW>,<&gpio0GPIO_C3GPIO_ACTIVE_LOW>;

然后是調(diào)用 devm_gpio_request 來申請 gpio,接著是調(diào)用 pinctrl_lookup_state 來查找 “gpio” 狀態(tài),而默認(rèn)狀態(tài) “default” 已經(jīng)由框架保存到 i2c->dev-pins->default_state 中了。最后調(diào)用 pinctrl_select_state 來選擇是 “default” 還是 “gpio” 功能。

下面是常用的GPIO復(fù)用 API的定義:

#include structdevice{//...#ifdef CONFIG_PINCTRLstructdev_pin_info*pins;#endif//...};structdev_pin_info{structpinctrl*p;structpinctrl_state*default_state;#ifdef CONFIG_PMstructpinctrl_state*sleep_state;structpinctrl_state*idle_state;#endif};structpinctrl_state*pinctrl_lookup_state(structpinctrl*p,constchar*name);intpinctrl_select_state(structpinctrl*p,structpinctrl_state*s);

gslx680 驅(qū)動解析之輸入輸出,中斷

以下是對px30源SDK中g(shù)slx680外設(shè)驅(qū)動中g(shù)sl_ts_probe()函數(shù),gslX680_init()函數(shù),static irqreturn_t gsl_ts_irq()進行部分的解析,用戶可以從中了解gpio的輸入輸出,中斷功能的使用,而用于復(fù)用功能的i2c通信,則在下一章i2c進行講解。

static int gsl_ts_probe(struct i2c_client *client,const struct i2c_device_id *id) { ...... struct gsl_ts *ts; struct device_node *np = client->dev.of_node; //設(shè)備節(jié)點結(jié)構(gòu)體 enum of_gpio_flags wake_flags; unsigned long irq_flags; ...... ts->irq_pin=of_get_named_gpio_flags(np, "touch-gpio", 0, (enum of_gpio_flags *)&irq_flags); //讀取設(shè)備樹 ts->wake_pin=of_get_named_gpio_flags(np, "reset-gpio", 0, &wake_flags); //讀取設(shè)備樹 /*申請gpio資源*/ if (gpio_is_valid(ts->wake_pin)) { rc = devm_gpio_request_one(&client->dev, ts->wake_pin, (wake_flags & OF_GPIO_ACTIVE_LOW) ? GPIOF_OUT_INIT_LOW : GPIOF_OUT_INIT_HIGH, "gslX680 wake pin"); if (rc != 0) { dev_err(&client->dev, "gslX680 wake pin error\n"); return -EIO; } g_wake_pin = ts->wake_pin; } else { dev_info(&client->dev, "wake pin invalid\n"); } /*申請gpio資源*/ if (gpio_is_valid(ts->irq_pin)) { rc = devm_gpio_request_one(&client->dev, ts->irq_pin, (irq_flags & OF_GPIO_ACTIVE_LOW) ? GPIOF_OUT_INIT_LOW : GPIOF_OUT_INIT_HIGH, "gslX680 irq pin"); if (rc != 0) { dev_err(&client->dev, "gslX680 irq pin error\n"); return -EIO; } g_irq_pin = ts->irq_pin; } ...... gslX680_init(); //對復(fù)位引腳與中斷引腳進行初始化操作 ...... /*申請中斷IRQ號,綁定中斷函數(shù)*/ ts->irq=gpio_to_irq(ts->irq_pin); if (ts->irq) { printk("zjy: ts->irq %d \r\n", ts->irq); rc = devm_request_threaded_irq(&client->dev, ts->irq, NULL, gsl_ts_irq, irq_flags | IRQF_ONESHOT, client->name, ts); if (rc != 0) { printk("zjy :Cannot allocate ts INT!ERRNO:%d\n", rc); goto error_req_irq_fail; } disable_irq(ts->irq); } else { printk("gsl x680 irq req fail\n"); goto error_req_irq_fail; } ...... }
  • of_get_named_gpio_flags 從設(shè)備樹中讀取 “reset-gpio” 和 “touch-gpio” 的 GPIO 配置編號和標(biāo)志,gpio_is_valid 判斷該 GPIO 編號是否有效,devm_gpio_request_one則申請占用該 GPIO。如果初始化過程出錯,會跳到dev_err()函數(shù)進行報錯與gpio資源釋放處理。

  • 調(diào)用gpio_to_irq把GPIO的PIN值轉(zhuǎn)換為相應(yīng)的IRQ值,調(diào)用devm_request_threaded_irq申請中斷,如果失敗會goto到標(biāo)簽error_req_irq_fail進行錯誤處理,gpio資源的釋放,該函數(shù)中ts->irq是要申請的硬件中斷號,gsl_ts_irq是中斷函數(shù),irq_flags | IRQF_ONESHOT是中斷標(biāo)志位, client->name是設(shè)備驅(qū)動程序名稱,ts是該設(shè)備的device結(jié)構(gòu)體,在注冊共享中斷時會用到。

在gsl_ts_probe()中,會在上電的時候,對復(fù)位引腳以及中斷引腳進行初始化操作:

static int gslX680_init(void) { gpio_direction_output(g_wake_pin, 0); //設(shè)置該引腳為輸出模式,輸出0 msleep(20); gpio_set_value(g_wake_pin,1); //設(shè)置引腳的輸出值為1; msleep(20); gpio_set_value(g_irq_pin,1); //設(shè)置中斷引腳的初始值為 1 msleep(20); return 0; }

由上面的步驟可知曉,在設(shè)備上電的時候,可以用示波器測試出,該reset引腳會出現(xiàn)一個復(fù)位的操作。

而對于中斷而言,在用戶在進入系統(tǒng)之后,點擊觸摸屏,會把中斷引腳拉低,由上面的devm_request_threaded_irq()函數(shù)可知,該中斷在觸發(fā)的時候,會跳到static irqreturn_t gsl_ts_irq()中斷函數(shù)中去執(zhí)行:

static irqreturn_t gsl_ts_irq(int irq, void *dev_id) { struct gsl_ts *ts = dev_id; disable_irq_nosync(ts->irq); if (!work_pending(&ts->work)) { queue_work(ts->wq, &ts->work); } printk("Enter firefly gpio irq test program!\n"); //在進入中斷函數(shù)后,會打印這一句話,但源碼未添加,用戶可以自行添加來驗證。 return IRQ_HANDLED; }

上面這個中斷函數(shù)主要是做了一些鍵值的上報,由于本文未涉及input輸入子系統(tǒng),所以不在此處講述。

下面是常用的 GPIO 輸入輸出的API 定義:

#include #include enumof_gpio_flags{OF_GPIO_ACTIVE_LOW=0x1,};intof_get_named_gpio_flags(structdevice_node*np,constchar*propname,intindex,enumof_gpio_flags*flags);intgpio_is_valid(intgpio);intgpio_request(unsignedgpio,constchar*label);voidgpio_free(unsignedgpio);intgpio_direction_input(intgpio);intgpio_direction_output(intgpio,intv);
調(diào)試方法

IO指令

GPIO調(diào)試有一個很好用的工具,那就是IO指令,Android系統(tǒng)默認(rèn)已經(jīng)內(nèi)置了IO指令,使用IO指令可以實時讀取或?qū)懭朊總€IO口的狀態(tài),這里簡單介紹IO指令的使用。 首先查看 io 指令的幫助:

#io --help Unknown option: ? Raw memory i/o utility - $Revision: 1.5 $ io -v -1|2|4 -r|w [-l] [-f][] -v Verbose, asks for confirmation -1|2|4 Sets memory access size in bytes (default byte) -lLength in bytes of area to access (defaults to one access, or whole file length) -r|w Read from or Write to memory (default read) -fFile to write on memory read, or to read on memory writeThe memory address to accessThe value to write (implies -w) Examples: io 0x1000 Reads one byte from 0x1000 io 0x1000 0x12 Writes 0x12 to location 0x1000 io -2 -l 8 0x1000 Reads 8 words from 0x1000 io -r -f dmp -l 100 200 Reads 100 bytes from addr 200 to file io -w -f img 0x10000 Writes the whole of file to memory Note access size (-1|2|4) does not apply to file based accesses.

從幫助上可以看出,如果要讀或者寫一個寄存器,可以用:

io-4-r0x1000//讀從0x1000起的4位寄存器的值io-4-w0x1000//寫從0x1000起的4位寄存器的值

使用示例:

  • 查看GPIO0當(dāng)前各引腳值的情況

  • 從主控的datasheet查到GPIO0_IOMUX對應(yīng)寄存器基地址為:FF040000

# io -4 -r 0xff040000ff040000:00003807
  • 如果想改變GPIO的配置值,可以使用以下指令設(shè)置:

# io -4 -w 0xff040000 0xxxxxxxxx(你想要設(shè)置的值,例如0x00001101)

GPIO調(diào)試接口

Debugfs文件系統(tǒng)目的是為開發(fā)人員提供更多內(nèi)核數(shù)據(jù),方便調(diào)試。 這里GPIO的調(diào)試也可以用Debugfs文件系統(tǒng),獲得更多的內(nèi)核信息。 GPIO在Debugfs文件系統(tǒng)中的接口為 /sys/kernel/debug/gpio,可以這樣讀取該接口的信息:

px30_evb:/ # cat /sys/kernel/debug/gpio GPIOs 0-31, platform/pinctrl, gpio0: gpio-0 ( |speak_gpio ) out hi gpio-1 ( |bt_default_wake_host) in hi gpio-2 ( |reset ) out hi gpio-5 ( |gslX680 irq pin ) in hi gpio-11 ( |bt_default_wake ) in hi gpio-12 ( |gslX680 wake pin ) out hi gpio-13 ( |enable ) out hi GPIOs 32-63, platform/pinctrl, gpio1: gpio-40 ( |headset_gpio ) in lo gpio-44 ( |? ) out lo gpio-45 ( |? ) out hi gpio-47 ( |camsys_gpio ) out hi gpio-51 ( |bt_default_rts ) in lo GPIOs 64-95, platform/pinctrl, gpio2: gpio-72 ( |bt_default_reset ) out lo gpio-77 ( |mdio-reset ) out hi GPIOs 96-127, platform/pinctrl, gpio3: GPIOs 511-511, platform/rk805-pinctrl, rk817-gpio, can sleep:

從讀取到的信息中可以知道,內(nèi)核把GPIO當(dāng)前的狀態(tài)都列出來了,以GPIO0組為例,gpio-5(GPIO0_A5)作為gslX680 模塊的中斷引腳,設(shè)置輸入,輸出高電平。

FAQs

Q1: 如何將PIN的MUX值切換為一般的GPIO?

A1: 當(dāng)使用GPIO request時候,會將該PIN的MUX值強制切換為GPIO,所以使用該pin腳為GPIO功能的時候確保該pin腳沒有被其他模塊所使用。

Q2: 為什么我用IO指令讀出來的值都是0x00000000?

A2: 如果用IO命令讀某個GPIO的寄存器,讀出來的值異常,如 0x00000000或0xffffffff等,請確認(rèn)該GPIO的CLK是不是被關(guān)了,GPIO的CLK是由CRU控制,可以通過讀取datasheet下面CRU_CLKGATE_CON* 寄存器來查到CLK是否開啟,如果沒有開啟可以用io命令設(shè)置對應(yīng)的寄存器,從而打開對應(yīng)的CLK,打開CLK之后應(yīng)該就可以讀到正確的寄存器值了。

Q3: 測量到PIN腳的電壓不對應(yīng)該怎么查?

A3: 測量該PIN腳的電壓不對時,如果排除了外部因素,可以確認(rèn)下該pin所在的io電壓源是否正確,以及IO-Domain配置是否正確。

Q4: gpio_set_value()與gpio_direction_output()有什么區(qū)別?

A4: 如果使用該GPIO時,不會動態(tài)的切換輸入輸出,建議在開始時就設(shè)置好GPIO 輸出方向,后面拉高拉低時使用gpio_set_value()接口,而不建議使用gpio_direction_output(), 因為gpio_direction_output接口里面有mutex鎖,對中斷上下文調(diào)用會有錯誤異常,且相比 gpio_set_value,gpio_direction_output 所做事情更多,浪費。

I2C 使用
簡介

AIO-PX30-JD4 開發(fā)板上有 4 個片上 I2C 控制器,各個 I2C 的使用情況如下表:

PortPinnameDeviceI2C0GPIO0_B0/I2C_SCLRK809GPIO0_B1/I2C_SDAI2C1GPIO0_C2/I2C_SCLGS_MC3230CPIO0_C3/I2C_SDAI2C2GPIO2_B7/I2C_SCLGSLX680GPIO2_C0/I2C_SDAI2C3GPIO1_B4/I2C_SDA復(fù)用為其他功能GPIO1_B5/I2C_SCL

本文主要描述如何在該開發(fā)板上配置 I2C。

配置 I2C 可分為兩大步驟:

  • 定義和注冊 I2C 設(shè)備

  • 定義和注冊 I2C 驅(qū)動

下面以配置 GSL3680 (觸摸屏)為例。

定義和注冊 I2C 設(shè)備

在注冊I2C設(shè)備時,需要結(jié)構(gòu)體 i2c_client 來描述 I2C 設(shè)備。然而在標(biāo)準(zhǔn)Linux中,用戶只需要提供相應(yīng)的 I2C 設(shè)備信息,Linux就會根據(jù)所提供的信息構(gòu)造 i2c_client 結(jié)構(gòu)體。

用戶所提供的 I2C 設(shè)備信息以節(jié)點的形式寫到 dts 文件中,路徑為 kernel/arch/arm64/boot/dts/rockchip/px30-firefly-aiojd4-lvds.dts ,如下所示:

&i2c2{status="okay";......gslx680:gslx680@41{compatible="gslX680";reg=<0x41>;screen_max_x=<800>;screen_max_y=<1280>;touch-gpio=<&gpio05IRQ_TYPE_LEVEL_LOW>;reset-gpio=<&gpio012GPIO_ACTIVE_HIGH>;flip-x=<1>;flip-y=<0>;swap-xy=<0>;gsl,fw=<1>;};......};
定義和注冊 I2C 驅(qū)動

該驅(qū)動的路徑為:kernel/drivers/input/touchscreen/gslx680_firefly.c

定義 I2C 驅(qū)動

在定義 I2C 驅(qū)動之前,用戶首先要定義變量 of_device_id 和 i2c_device_id 。

of_device_id 用于在驅(qū)動中調(diào)用dts文件中定義的設(shè)備信息,其定義如下所示:

staticstructof_device_idgsl_ts_ids[]={{.compatible="gslX680"},{}};

定義變量 i2c_device_id:

staticconststructi2c_device_idgsl_ts_id[]={{GSLX680_I2C_NAME,0},{}};MODULE_DEVICE_TABLE(i2c,gsl_ts_id);

i2c_driver 如下所示:

staticstructi2c_drivergsl_ts_driver={.driver={.name=GSLX680_I2C_NAME,.owner=THIS_MODULE,.of_match_table=of_match_ptr(gsl_ts_ids),},#ifndef CONFIG_HAS_EARLYSUSPEND//.suspend=gsl_ts_suspend,//.resume=gsl_ts_resume,#endif.probe=gsl_ts_probe,.remove=gsl_ts_remove,.id_table=gsl_ts_id,};

注:變量id_table指示該驅(qū)動所支持的設(shè)備。

注冊 I2C 驅(qū)動

使用i2c_add_driver函數(shù)注冊 I2C 驅(qū)動。

i2c_add_driver(&gsl_ts_driver);

在調(diào)用 i2c_add_driver 注冊 I2C 驅(qū)動時,會遍歷 I2C 設(shè)備,如果該驅(qū)動支持所遍歷到的設(shè)備(即id_table的值與設(shè)備樹的compatible屬性值相同),則會調(diào)用該驅(qū)動的 probe 函數(shù)。

通過 I2C 收發(fā)數(shù)據(jù)

在注冊好 I2C 驅(qū)動后,即可進行 I2C 通訊。

在該驅(qū)動的gsl_ts_probe()函數(shù)中,會對gslx680的IC進行初始化,而在初始化的代碼中,會對主從設(shè)備的通訊進行一個測試

gsl_ts_probe()->init_chip()->test_i2c().

而在 test_i2c()這個函數(shù)中,會存在gsl_ts_read(),gsl_ts_write()兩個gslx680驅(qū)動自己封裝的主機發(fā)送和主機接受函數(shù),其內(nèi)部真正調(diào)用的是Linux內(nèi)核提供的I2C通訊函數(shù)。

  • 向從機發(fā)送信息:

int i2c_master_send(const struct i2c_client *client, const char *buf, int count) { int ret; struct i2c_adapter *adap = client->adapter; struct i2c_msg msg; msg.addr = client->addr; msg.flags = client->flags & I2C_M_TEN; msg.len = count; msg.buf = (char *)buf; ret = i2c_transfer(adap, &msg, 1); /* + If everything went ok (i.e. 1 msg transmitted), return #bytes + transmitted, else error code. */ return (ret == 1) ? count : ret; }
  • 向從機讀取信息:

int i2c_master_recv(const struct i2c_client *client, char *buf, int count) { struct i2c_adapter *adap = client->adapter; struct i2c_msg msg; int ret; msg.addr = client->addr; msg.flags = client->flags & I2C_M_TEN; msg.flags |= I2C_M_RD; msg.len = count; msg.buf = buf; ret = i2c_transfer(adap, &msg, 1); /* + If everything went ok (i.e. 1 msg received), return #bytes received, + else error code. */ return (ret == 1) ? count : ret; } EXPORT_SYMBOL(i2c_master_recv);

在使用i2c_master_xxx()函數(shù)來進行接受或者發(fā)送的時候,也是調(diào)用i2c_transfer()這個函數(shù)來處理一個消息結(jié)構(gòu)體(i2c_msg),而對于一些處理信息比較復(fù)雜的I2C設(shè)備,可以直接調(diào)用i2c_transfer()來處理信息,不過要自己構(gòu)造 i2c_msg 結(jié)構(gòu)體。

struct i2c_msg { __u16 addr; //IIC從設(shè)備地址 __u16 flags;//操作標(biāo)志位,I2C_M_RD為讀(1),寫為0 #define I2C_M_TEN 0x0010 /* this is a ten bit chip address */ #define I2C_M_RD 0x0001 /* read data, from slave to master */ #define I2C_M_NOSTART 0x4000 /* if I2C_FUNC_PROTOCOL_MANGLING */ #define I2C_M_REV_DIR_ADDR 0x2000 /* if I2C_FUNC_PROTOCOL_MANGLING */ #define I2C_M_IGNORE_NAK 0x1000 /* if I2C_FUNC_PROTOCOL_MANGLING */ #define I2C_M_NO_RD_ACK 0x0800 /* if I2C_FUNC_PROTOCOL_MANGLING */ #define I2C_M_RECV_LEN 0x0400 /* length will be first received byte */ __u16 len; //傳輸?shù)臄?shù)據(jù)長度,字節(jié)為單位 __u8 *buf; //存放read或write的數(shù)據(jù)的buffer };
FAQs

Q1: 通信失敗,出現(xiàn)這種log:”timeout, ipd: 0x00, state: 1”該如何調(diào)試?

A1: 請檢查硬件上拉是否給電。

Q2: 調(diào)用i2c_transfer返回值為-6?

A2: 返回值為-6表示為NACK錯誤,即對方設(shè)備無應(yīng)答響應(yīng),這種情況一般為外設(shè)的問題,常見的有以下幾種情況:

  • I2C地址錯誤,解決方法是測量I2C波形,確認(rèn)是否I2C 設(shè)備地址錯誤;

  • I2C slave 設(shè)備不處于正常工作狀態(tài),比如未給電,錯誤的上電時序等;

  • 時序不符合 I2C slave設(shè)備所要求也會產(chǎn)生Nack信號。

Q3: 當(dāng)外設(shè)對于讀時序要求中間是stop信號不是repeat start信號的時候,該如何處理?

A3: 這時需要調(diào)用兩次i2c_transfer, I2C read 拆分成兩次,修改如下:

static int i2c_read_bytes(struct i2c_client *client, u8 cmd, u8 *data, u8 data_len) { struct i2c_msg msgs[2]; int ret; u8 *buffer; buffer = kzalloc(data_len, GFP_KERNEL); if (!buffer) return -ENOMEM;; msgs[0].addr = client->addr; msgs[0].flags = client->flags; msgs[0].len = 1; msgs[0].buf = &cmd; ret = i2c_transfer(client->adapter, msgs, 1); if (ret < 0) { dev_err(&client->adapter->dev, "i2c read failed\n"); kfree(buffer); return ret; } msgs[1].addr = client->addr; msgs[1].flags = client->flags | I2C_M_RD; msgs[1].len = data_len; msgs[1].buf = buffer; ret = i2c_transfer(client->adapter, &msgs[1], 1); if (ret < 0) dev_err(&client->adapter->dev, "i2c read failed\n"); else memcpy(data, buffer, data_len); kfree(buffer); return ret; }
IR 使用
紅外遙控配置

AIO-PX30-JD4 開發(fā)板上使用紅外收發(fā)傳感器 IR (麥克風(fēng)和i2c0之間)實現(xiàn)遙控功能,在IR接口處接上紅外接收器。本文主要描述在開發(fā)板上如何配置紅外遙控器。

其配置步驟可分為兩個部分:

  • 修改內(nèi)核驅(qū)動:內(nèi)核空間修改,Linux 和 Android 都要修改這部分的內(nèi)容。

  • 修改鍵值映射:用戶空間修改(僅限 Android 系統(tǒng))。

配置DTS

在PX30的DTS文件 : kernel/arch/arm64/boot/dts/rockchip/px30-firefly-aiojd4-lvds.dts 中:

&pwm3{status="okay";interrupts=;compatible="rockchip,remotectl-pwm";remote_pwm_id=<3>;handle_cpu_id=<0>;remote_support_psci=<1>;ir_key1{rockchip,usercode=<0xff00>;rockchip,key_table=<0xebKEY_POWER>,<0xecKEY_MENU>,<0xfeKEY_BACK>,<0xb7KEY_HOME>,<0xa3KEY_WWW>,<0xf4KEY_VOLUMEUP>,<0xa7KEY_VOLUMEDOWN>,<0xf8KEY_REPLY>,<0xfcKEY_UP>,<0xfdKEY_DOWN>,<0xf1KEY_LEFT>,<0xe5KEY_RIGHT>;};};

注1:第一列為鍵值,第二列為要響應(yīng)的按鍵碼。 注2:由于UART3的RX與IR復(fù)用了,所以要使用IR功能,就需要在設(shè)備樹上關(guān)閉UART3。

&uart3{......status="disabled"......};
內(nèi)核驅(qū)動

在 Linux 內(nèi)核中,IR 驅(qū)動僅支持 NEC 編碼格式。以下是在內(nèi)核中配置紅外遙控的方法。

所涉及到的文件

drivers/input/remotectl/rockchip_pwm_remotectl.c

如何獲取用戶碼和IR 鍵值

在 remotectl_do_something 函數(shù)中獲取用戶碼和鍵值:

caseRMC_USERCODE:{//ddata->scanData<<=1;//ddata->count++;if((RK_PWM_TIME_BIT1_MINperiod)&&(ddata->periodscanData|=(0x01<count);}ddata->count++;if(ddata->count==0x10){//16bitusercodeDBG_CODE("GET USERCODE=0x%x\n",((ddata->scanData)&0xffff));if(remotectl_keybdNum_lookup(ddata)){ddata->state=RMC_GETDATA;ddata->scanData=0;ddata->count=0;}else{//usercodeerrorddata->state=RMC_PRELOAD;}}}

注:用戶可以使用 DBG_CODE() 函數(shù)打印用戶碼。

使用下面命令可以使能DBG_CODE打?。?/p>

echo1>/sys/module/rockchip_pwm_remotectl/parameters/code_print

將 IR 驅(qū)動編譯進內(nèi)核

將 IR 驅(qū)動編譯進內(nèi)核的步驟如下所示:

(1)、向配置文件 drivers/input/remotectl/Kconfig 中添加如下配置:

configROCKCHIP_REMOTECTL_PWMbool"rockchip remoctrl pwm capture"defaultn

(2)、修改 drivers/input/remotectl 路徑下的 Makefile,添加如下編譯選項:

obj-$(CONFIG_ROCKCHIP_REMOTECTL_PWM) += rockchip_pwm_remotectl.o

(3)、在 kernel 路徑下使用 make menuconfig ,按照如下方法將IR驅(qū)動選中。

DeviceDrivers--->Inputdevicesupport----->[*]rockchipremotectl---------->[*]rockchipremoctrlpwmcapture

保存后,執(zhí)行 make 命令即可將該驅(qū)動編進內(nèi)核。

Android 鍵值映射

文件 /system/usr/keylayout/ff200030_pwm.kl 用于將 Linux 層獲取的鍵值映射到 Android 上對應(yīng)的鍵值。用戶可以添加或者修改該文件的內(nèi)容以實現(xiàn)不同的鍵值映射。

該文件內(nèi)容如下所示:

key28ENTERkey116POWERkey158BACKkey139MENUkey217SEARCHkey232DPAD_CENTERkey108DPAD_DOWNkey103DPAD_UPkey102HOMEkey105DPAD_LEFTkey106DPAD_RIGHTkey115VOLUME_UPkey114VOLUME_DOWNkey143NOTIFICATIONkey113VOLUME_MUTEkey388TV_KEYMOUSE_MODE_SWITCH

注:通過 adb 修改該文件重啟后即可生效。

IR 觸發(fā)

下圖是當(dāng)紅外遙控器按鈕按下的時候,所產(chǎn)生的波形,主要由head,Control,information,signed free這四部分組成,具體可以參考RC6 Protocol。

實物連接圖

LCD使用
簡介

AIO-PX30-JD4開發(fā)板默認(rèn)外置支持了一個LCD屏接口,為LVDS,另外板子也支持MIPI屏幕,但需要注意的是MIPI和LVDS是復(fù)用的,使用LVDS之后不能使用MIPI,接口如下圖:

Config配置

由于AIO-PX30-JD4默認(rèn)使用的是LVDS屏幕,同時在默認(rèn)的配置文件kernel/arch/arm64/configs/firefly_defconfig已經(jīng)把LCD相關(guān)的配置設(shè)置好了,如果自己做了修改,請注意把以下配置加上:

CONFIG_ROCKCHIP_DW_MIPI_DSI=y......CONFIG_ROCKCHIP_LVDS=y
DTS配置

DSI_PHY配置

AIO-PX30-JD4中關(guān)于LVDS(MIPI) DSI_PHY的DTS配置在:kernel/arch/arm64/boot/dts/rockchip/px30.dtsi中,從該文件我們可以看到:

......display_subsystem:display-subsystem{compatible="rockchip,display-subsystem";ports=<&vopb_out>,<&vopl_out>;status="disabled";};......lvds:lvds@ff2e0000{compatible="rockchip,px30-lvds";reg=<0x00xff2e00000x00x10000>,<0x00xff4500000x00x10000>;clocks=<&cruPCLK_MIPIDSIPHY>,<&cruPCLK_MIPI_DSI>;clock-names="pclk_lvds","pclk_lvds_ctl";power-domains=<&powerPX30_PD_VO>;rockchip,grf=<&grf>;status="disabled";ports{#address-cells = <1>;#size-cells = <0>;port@0{reg=<0>;#address-cells = <1>;#size-cells = <0>;lvds_in_vopb:endpoint@0{reg=<0>;remote-endpoint=<&vopb_out_lvds>;};lvds_in_vopl:endpoint@1{reg=<1>;remote-endpoint=<&vopl_out_lvds>;};};};};......

而在kernel/arch/arm64/boot/dts/rockchip/px30-firefly-aiojd4-lvds.dts 也存在對以上dts進行引用配置

&display_subsystem{status="okay";//Turnonthedisplaysubsystem};&lvds{status="okay";//openlvdsfunctionports{port@1{reg=<1>;lvds_out_panel:endpoint{remote-endpoint=<&panel_in_lvds>;};};};};&lvds_in_vopl{status="disabled";};&lvds_in_vopb{status="okay";};&route_lvds{status="okay";};

Backlight配置

AIO-PX30-JD4開發(fā)板外置了一個背光接口用來控制屏幕背光,如下圖所示:

主要有背光電源引腳以及控制亮度引腳,DTS:kernel/arch/arm64/boot/dts/rockchip/px30-firefly-aiojd4-lvds.dts配置如下

......backlight:backlight{status="okay";compatible="pwm-backlight";enable-gpios=<&gpio013GPIO_ACTIVE_HIGH>;pwms=<&pwm00500000>;brightness-levels=;default-brightness-level=<200>;};......
  • enable-gpios 屬性為背光的電源控制引腳。

  • pwms屬性:配置PWM,可用來改變輸出占空比(范例里面默認(rèn)使用pwm0,50000ns是周期(20 KHz)。

  • brightness-levels屬性:配置背光亮度數(shù)組,最大值為255,配置暗區(qū)和亮區(qū),并把亮區(qū)數(shù)組做255的比例調(diào)節(jié)。比如范例中暗區(qū)是255-221,亮區(qū)是220-0。 由于PX30使用200以上的level屏幕就會過暗,所以默認(rèn)最大值為200。

  • default-brightness-level屬性:開機時默認(rèn)背光亮度,范圍為0-255。

具體請參考kernel中的說明文檔:kernel/Documentation/devicetree/bindings/leds/backlight/pwm-backlight.txt

顯示時序配置

在kernel/arch/arm64/boot/dts/rockchip/px30-firefly-aiojd4-lvds.dts中可以看到以下語句:

......display-timings{native-mode=<&timing0>;timing0:timing0{clock-frequency=<65000000>;hactive=<800>;vactive=<1280>;hfront-porch=<32>;hsync-len=<8>;hback-porch=<8>;vfront-porch=<17>;vsync-len=<4>;vback-porch=<11>;hsync-active=<0>;vsync-active=<0>;de-active=<0>;pixelclk-active=<0>;};};......

時序?qū)傩詤⒖枷聢D:

lvds屏上完電后需要完成一些初始化的工作才可以工作。

  • kernel 部分 -> kernel/drivers/gpu/drm/panel/panel-simple.c

staticintpanel_simple_enable(structdrm_panel*panel){structpanel_simple*p=to_panel_simple(panel);interr=0;if(p->enabled)return0;if(p->cmd_type==CMD_TYPE_MCU){err=panel_simple_mcu_send_cmds(p,p->on_cmds);if(err)dev_err(p->dev,"failed to send mcu on cmds\n");}if(p->desc&&p->desc->delay.enable)panel_simple_sleep(p->desc->delay.enable);backlight_enable(p->backlight);p->enabled=true;return0;}

u-boot 部分 ->u-boot/drivers/video/drm/rockchip-dw-mipi-dsi.c

staticvoiddw_mipi_dsi_enable(structdw_mipi_dsi*dsi){conststructdrm_display_mode*mode=dsi->mode;dsi_update_bits(dsi,DSI_LPCLK_CTRL,PHY_TXREQUESTCLKHS,PHY_TXREQUESTCLKHS);dsi_write(dsi,DSI_PWR_UP,RESET);if(dsi->mode_flags&MIPI_DSI_MODE_VIDEO){dsi_update_bits(dsi,DSI_MODE_CFG,CMD_VIDEO_MODE,VIDEO_MODE);}else{dsi_write(dsi,DSI_DBI_VCID,DBI_VCID(dsi->channel));dsi_update_bits(dsi,DSI_CMD_MODE_CFG,DCS_LW_TX,0);dsi_write(dsi,DSI_EDPI_CMD_SIZE,mode->hdisplay);dsi_update_bits(dsi,DSI_MODE_CFG,CMD_VIDEO_MODE,COMMAND_MODE);}dsi_write(dsi,DSI_PWR_UP,POWERUP);if(dsi->slave)dw_mipi_dsi_enable(dsi->slave);}

詳細(xì)流程說明可參考以下附件: Rockchip DRM Panel Porting Guide.pdf

LED 使用
前言

AIO-PX30-JD4 開發(fā)板上有 2 個 LED 燈,如下表所示:

LEDGPIOref.GPIOnumberBlueGPIO1_B545YellowGPIO1_B444

以設(shè)備的方式控制 LED可通過使用 LED 設(shè)備子系統(tǒng)或者直接操作 GPIO 控制該 LED。

標(biāo)準(zhǔn)的 Linux 專門為 LED 設(shè)備定義了 LED 子系統(tǒng)。 在 AIO-PX30-JD4 開發(fā)板中的兩個 LED 均以設(shè)備的形式被定義。

用戶可以通過 /sys/class/leds/ 目錄控制這兩個 LED。

開發(fā)板上的 LED 的默認(rèn)狀態(tài)為:

  • Blue: 系統(tǒng)上電時打開

  • Yellow:用戶自定義

用戶可以通過 echo 向其 brightness屬性輸入命令控制每一個 LED:

px30_evb:/# echo 0 >/sys/class/leds/firefly:blue:power/brightness //藍燈滅px30_evb:/# echo 1 >/sys/class/leds/firefly:blue:power/brightness //藍燈亮
使用trigger 方式控制 LED

Trigger 包含多種方式可以控制LED,這里就用兩個例子來說明

  • Simple trigger LED

  • Complex trigger LED

更詳細(xì)的說明請參考 kernel/Documentation/leds/leds-class.txt ,有內(nèi)核對LED相關(guān)功能的支持的描述。

首先我們需要知道定義多少個LED,同時對應(yīng)的LED的屬性是什么。

在 kernel/arch/arm64/boot/dts/rockchip/px30-firefly-aiojd4-lvds.dts 文件中定義LED節(jié)點,具體定義如下:

leds{compatible="gpio-leds";power_led::power{label="firefly:blue:power";linux,default-trigger="ir-power-click";default-state="on";gpios=<&gpio113GPIO_ACTIVE_HIGH>;pinctrl-names="default";pinctrl-0=<&led_power>;};user_led:user{label="firefly:yellow:user";linux,default-trigger="ir-user-click";default-state="off";gpios=<&gpio112GPIO_ACTIVE_HIGH>;pinctrl-names="default";pinctrl-0=<&led_user>;};};

注意:compatible 的值要跟 drivers/leds/leds-gpio.c 中的 .compatible 的值要保持一致。

Simple trigger LED

這是使用簡單的觸發(fā)方式控制來LED,如下就默認(rèn)打開黃燈:

(1)定義 LED 觸發(fā)器 在kernel/drivers/leds/trigger/led-firefly-demo.c 文件中有如下添加

DEFINE_LED_TRIGGER(ledtrig_default_control);

(2)注冊該觸發(fā)器

led_trigger_register_simple("ir-user-click",&ledtrig_default_control);

(3)控制 LED 的亮。

led_trigger_event(ledtrig_default_control,LED_FULL);//yellowledonenumled_brightness{LED_OFF=0,//關(guān)閉LEDLED_HALF=127,//LED設(shè)置為一半的亮度LED_FULL=255,//LED設(shè)置為全部的亮度};

(4)打開LED demo

led-firefly-demo默認(rèn)沒有打開,如果需要的話可以使用以下補丁打開demo驅(qū)動:

---a/kernel/arch/arm64/boot/dts/rockchip/px30-firefly-demo.dtsi+++b/kernel/arch/arm64/boot/dts/rockchip/px30-firefly-demo.dtsi@@-52,7+52,7@@led_demo:led_demo{-status="disabled";+status="okay";compatible="px30-led-demo";};

Complex trigger LED

如下是trigger方式控制LED復(fù)雜一點的例子,timer trigger 就是讓LED達到不斷亮滅的效果

我們需要在內(nèi)核把timer trigger配置上

在 kernel 路徑下使用 make menuconfig ,按照如下方法將timer trigger驅(qū)動選中。

DeviceDrivers--->LEDSupport--->LEDTriggersupport--->LEDTimerTrigger

保存配置并編譯內(nèi)核,把kernel.img 燒到AIO-PX30-JD4板子上 我們可以使用串口輸入命令,就可以看到藍燈不停的間隔閃爍

echo"timer">sys/class/leds/firefly\:blue\:power/trigger

用戶還可以使用 cat 命令獲取 trigger 的可用值:

px30_evb:/# cat sys/class/leds/firefly\:blue\:power/triggernonerc-feedbacktest_ac-onlinetest_battery-charging-or-fulltest_battery-chargingtest_battery-fulltest_battery-charging-blink-full-solidtest_usb-onlinemmc0mmc1ir-user-click[timer]heartbeatbacklightdefault-onrfkill0mmc2rfkill1rfkill2
MIPI CSI 使用
簡介

AIO-PX30-JD4 開發(fā)板帶有一個MIPI camera,為MIPI_CSI,MIPI最高支持 3264x2448 pixels拍照。

本文以 OV13850 攝像頭為例,講解在該開發(fā)板上的配置過程。

接口效果圖

DTS配置

kernel/arch/arm64/boot/dts/rockchip/px30.dtsi:

rk_isp:rk_isp@ff4a0000{compatible="rockchip,px30-isp","rockchip,isp";reg=<0x00xff4a00000x00x8000>;interrupts=;clocks=<&cruACLK_ISP>,<&cruHCLK_ISP>,<&cruSCLK_ISP>,<&cruSCLK_ISP>,<&cruPCLK_ISP>,<&cruSCLK_CIF_OUT>,<&cruSCLK_CIF_OUT>,<&cruPCLK_MIPICSIPHY>;clock-names="aclk_isp","hclk_isp","clk_isp","clk_isp_jpe","pclkin_isp","clk_cif_pll","clk_cif_out","pclk_dphyrx";resets=<&cruSRST_ISP>,<&cruSRST_MIPICSIPHY_P>;reset-names="rst_isp","rst_mipicsiphy";power-domains=<&powerPX30_PD_VI>;pinctrl-names="default","isp_dvp8bit2","isp_dvp10bit","isp_dvp12bit";pinctrl-0=<&cif_clkout_m0>;pinctrl-1=<&dvp_d2d9_m0>;pinctrl-2=<&dvp_d2d9_m0&dvp_d10d11_m0>;pinctrl-3=<&dvp_d0d1_m0&dvp_d2d9_m0&dvp_d10d11_m0>;rockchip,isp,mipiphy=<1>;rockchip,isp,csiphy,reg=<0xff2f00000x4000>;rockchip,grf=<&grf>;rockchip,cru=<&cru>;rockchip,isp,iommu-enable=<1>;iommus=<&isp_mmu>;status="disabled";};
驅(qū)動說明

與攝像頭相關(guān)的代碼目錄如下:

Android: `- hardware/rockchip/camera/ |- CameraHal // 攝像頭的 HAL 源碼 `- SiliconImage // ISP 庫,包括所有支持模組的驅(qū)動源碼 `- isi/drv/OV13850 // OV13850 模組的驅(qū)動源碼 `- calib/OV13850.xml // OV13850 模組的調(diào)校參數(shù) `- hardware/rockchip/camera/Config/ |- cam_board_rk3326.xml // 攝像頭的參數(shù)設(shè)置 Kernel: |- kernel/drivers/media/video/rk_camsys // CamSys 驅(qū)動源碼 `- kernel/include/media/camsys_head.h
配置原理

設(shè)置攝像頭相關(guān)的引腳和時鐘,即可完成配置過程。

從以下攝像頭接口原理圖可知,需要配置的引腳有:CIF_PWR、DVP_PWR和MIPI_RST。

  • mipi接口

  • DVP_PWR 對應(yīng) PX30 的 GPIO1_B7;

  • CIF_PWR 對應(yīng) PX30 的 GPIO1_B6;

  • MIPI_RST對應(yīng) PX30 的 GPIO2_B3;

在開發(fā)板中,這三個引腳都是在 cam_board_rk3326.xml 中設(shè)置。

配置步驟

配置 Android

修改hardware/rockchip/camera/Config/cam_board_rk3326.xml 來注冊攝像頭:


聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報投訴
  • 嵌入式主板
    +關(guān)注

    關(guān)注

    7

    文章

    6084

    瀏覽量

    35162
  • 安卓
    +關(guān)注

    關(guān)注

    5

    文章

    2117

    瀏覽量

    56966
  • Firefly
    +關(guān)注

    關(guān)注

    2

    文章

    538

    瀏覽量

    6964
收藏 人收藏

    評論

    相關(guān)推薦

    AIO-PX30-JD4核心板上手教程

    1.1. 入手指南1.1.1. 配件AIO-PX30-JD4 的標(biāo)準(zhǔn)套裝包含以下配件:CORE-PX30-JD4 核心板一塊MB-JD4-RK3328&PX30 底板一塊銅管天
    發(fā)表于 07-19 16:32

    fireflyCORE-PX30-JD4燒寫工作介紹

    原始固件是一種能以逐位復(fù)制的方式燒寫到存儲設(shè)備的固件,是存儲設(shè)備的原始映像。原始固件一般燒寫到SD卡中,但也可以燒寫到eMMC中。燒寫原始固件有許多工具可以選用:
    的頭像 發(fā)表于 11-14 11:03 ?2115次閱讀
    <b class='flag-5'>fireflyCORE-PX30-JD4</b>燒寫工作<b class='flag-5'>介紹</b>

    fireflyCORE-PX30-JD4接口定義介紹

    AIO-PX30-JD4 提供了豐富的接口,主要包括:電源接口, 1 x USB3.0(device),6x USB2.0(接口×4,座子×2),以太網(wǎng),LVDS屏幕接口,TP觸摸接口,屏電壓跳線
    的頭像 發(fā)表于 12-19 16:36 ?1843次閱讀
    <b class='flag-5'>fireflyCORE-PX30-JD4</b>接口定義<b class='flag-5'>介紹</b>

    fireflyCORE-PX30-JD4編譯Buildroot 固件簡介

    編譯 Buildroot 固件 本章介紹 Buildroot 固件的編譯使用。
    的頭像 發(fā)表于 12-19 16:54 ?4325次閱讀

    fireflyCORE-PX30-JD4編譯 Ubuntu 固件簡介

    為了方便用戶的使用與開發(fā),官方提供了 Linux 開發(fā)的整套 SDK,本章詳細(xì)的說明 SDK 的具體用法。
    的頭像 發(fā)表于 12-20 08:45 ?2577次閱讀

    fireflyCORE-PX30-JD4燒寫固件簡介

    AIO-PX30-JD4 有靈活的啟動方式。一般情況下,除非硬件損壞,AIO-PX30-JD4 開發(fā)板是不會變磚的。
    的頭像 發(fā)表于 12-20 08:49 ?3400次閱讀

    fireflyCORE-PX30-JD4產(chǎn)品簡介

    采用PX30工業(yè)級64位低功耗處理器,擁有強大的硬解碼能力,以及豐富的接口,僅需擴展功能底板即可快速實現(xiàn)項目研產(chǎn),可適用于AIOT物聯(lián)網(wǎng)設(shè)備、車載中控、游藝/游戲設(shè)備、商顯一體設(shè)備等應(yīng)用領(lǐng)域
    的頭像 發(fā)表于 12-20 08:50 ?1577次閱讀

    Core PX30 JD4 AndroidTools燒寫工具

    電子發(fā)燒友網(wǎng)站提供《Core PX30 JD4 AndroidTools燒寫工具.txt》資料免費下載
    發(fā)表于 09-13 09:30 ?1次下載
    Core <b class='flag-5'>PX30</b> <b class='flag-5'>JD4</b> AndroidTools燒寫工具

    Core PX30 JD4工具DriverAssitant驅(qū)動助手

    電子發(fā)燒友網(wǎng)站提供《Core PX30 JD4工具DriverAssitant驅(qū)動助手.txt》資料免費下載
    發(fā)表于 09-13 09:28 ?10次下載
    Core <b class='flag-5'>PX30</b> <b class='flag-5'>JD4</b>工具DriverAssitant<b class='flag-5'>驅(qū)動</b>助手

    Core PX30 JD4 Android 8.1 SDK源碼分享

    電子發(fā)燒友網(wǎng)站提供《Core PX30 JD4 Android 8.1 SDK源碼分享.txt》資料免費下載
    發(fā)表于 09-13 09:26 ?16次下載
    Core <b class='flag-5'>PX30</b> <b class='flag-5'>JD4</b> Android 8.1 SDK源碼分享

    Core PX30 JD4源代碼Linux SDK分享

    電子發(fā)燒友網(wǎng)站提供《Core PX30 JD4源代碼Linux SDK分享.txt》資料免費下載
    發(fā)表于 09-13 09:24 ?6次下載
    Core <b class='flag-5'>PX30</b> <b class='flag-5'>JD4</b>源代碼Linux SDK分享

    Core PX30 JD4固件Android 8.1固件分享

    電子發(fā)燒友網(wǎng)站提供《Core PX30 JD4固件Android 8.1固件分享.txt》資料免費下載
    發(fā)表于 09-13 09:23 ?13次下載
    Core <b class='flag-5'>PX30</b> <b class='flag-5'>JD4</b>固件Android 8.1固件分享

    Core PX30 JD4固件Buildroot(GPT)文件分享

    電子發(fā)燒友網(wǎng)站提供《Core PX30 JD4固件Buildroot(GPT)文件分享.txt》資料免費下載
    發(fā)表于 09-13 09:22 ?0次下載
    Core <b class='flag-5'>PX30</b> <b class='flag-5'>JD4</b>固件Buildroot(GPT)文件分享

    Core PX30 JD4固件Ubuntu(GPT)文件分享

    電子發(fā)燒友網(wǎng)站提供《Core PX30 JD4固件Ubuntu(GPT)文件分享.txt》資料免費下載
    發(fā)表于 09-13 09:21 ?1次下載
    Core <b class='flag-5'>PX30</b> <b class='flag-5'>JD4</b>固件Ubuntu(GPT)文件分享

    Core PX30 JD4核心板產(chǎn)品規(guī)格書

    電子發(fā)燒友網(wǎng)站提供《Core PX30 JD4核心板產(chǎn)品規(guī)格書.txt》資料免費下載
    發(fā)表于 09-13 09:20 ?14次下載
    Core <b class='flag-5'>PX30</b> <b class='flag-5'>JD4</b>核心板產(chǎn)品規(guī)格書