fireflyCORE-PX30-JD4驱动开发介绍

驱动开发 adc 使用 简介 aio-px30jd4 开发板上的 ad 接口有两种,分别为:温度传感器 (temperature sensor)、逐次逼近adc (successive approximation register)。其中:
ts-adc(temperature sensor):支持两通道,时钟频率必须低于800khz
sar-adc(successive approximation register):支持三通道单端10位的sar-adc,时钟频率必须小于13mhz。
linux内核采用工业 i/o 子系统(iio子系统)来控制 adc,该子系统主要为 ad 转换或者 da 转换而设计。本文以配置sar-adc为例,主要介绍 sar-adc的基本配置和使用的方法,其相关的数据结构,源码路径以及配置步骤如下:
数据结构 iio_channel结构体 structiio_channel{structiio_dev*indio_dev;//工业i/o设备conststructiio_chan_spec*channel;//i/o通道void*data;};
iio_dev结构体 该结构体主要是用于描述io口所属的设备,其具体定义如下:
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结构体 该结构体主要用于描述单个通道的属性,具体定义如下:
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 此驱动通过解析内核设备树中的saradc资源,申请使用iio子系统来控制saradc。 * kernel/drivers/input/keyboard/adc-keys.c 此驱动通过判断adc通道的值范围,来判断哪个按键按下了。
配置步骤 配置dts节点 第一步:在aio-px30jd4的 dts 文件:kernel/arch/arm64/boot/dts/rockchip/px30.dtsi中,添加saradc资源,应如下:
saradc:saradc@ff288000{compatible=rockchip,px30-saradc,rockchip,rk3399-saradc;reg=;interrupts=;#io-channel-cells = ;clocks=,;clock-names=saradc,apb_pclk;resets=;reset-names=saradc-apb;status=disabled;};
第二步:根据用户的通道需要选择对应的saradc通道,本次例程使用aio-px30-jd4上的adc按键检测,选择通道2,配置如下:
adc-keys{compatible=adc-keys;io-channels=;io-channel-names=buttons;poll-interval=;keyup-threshold-microvolt=;vol-up-key{linux,code=;label=volume up;press-threshold-microvolt=;};};
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 属性 为 按键上报的键值,键值对应的动作 为 “音量+” 。
在saradc驱动文件中匹配 saradc 的dts 节点 第一步: 在内核设备树添加saradc资源之后,可以在源码kernel/drivers/iio/adc/rockchip_saradc.c中添加对应的saradc数据结构体
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驱动可以匹配到saradc设备。因如下:
static const struct of_device_id rockchip_saradc_match[] = { ...... { .compatible = rockchip,px30-saradc, //用于匹配px30的saradc设备 .data = &px30_saradc_data, //px30的saradc相关属性。 }, {}, ...... };
第三步: 填充saradc的platform_driver结构体:
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)宏平进行驱动的注册。
在设备上电的时候,内核会解析内核设备树,当检测到设备树上saradc的compatible属性与saradc驱动的of_device_id中的compatible成员一致的时候,便会调用rockchip_saradc.c中的rockchip_saradc_probe()函数来进行iio系统的adc设备的资源申请以及初始化(此处不再赘述,用户可自行查看源码)。
在进入系统后,会出现一个 /sys/bus/iio/devices/iio:device0的目录,表示创建成功。
在adc-keys.c驱动文件中匹配 adc-keys的dts 节点 第一步: 填充adc按键驱动的adc_keys_of_match[]中的compatible成员用于匹配设备
staticconststructof_device_idadc_keys_of_match[]={{.compatible=adc-keys,},{}};
第二步: 填充驱动结构体
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);往内核注册该驱动。
第四步: 设备树 compatible匹配正确,驱动注册成功之后,便会调用adc按键驱动的adc_keys_probe()函数,进行输入子系统设备的注册(因为是按键驱动,所以使用输入子系统,此部分不在此讲述)与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 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 ...... }
在进入系统后,通过 getevent 命令 :
adddevice1:/dev/input/event0name:rk8xx_pwrkeyadddevice2:/dev/input/event3name:adc-keysadddevice3:/dev/input/event1name:gslx680adddevice4:/dev/input/event2name:rk_headset
其中我们可以看到:
adddevice2:/dev/input/event3name:adc-keys
这样表示我们的设备已经创建成功。
按键检测 adc-keys.c驱动是通过输入子系统的轮询检测函数adc_keys_poll(),来不断地读取saradc通道的值,当不同按键按下的时候,是有不同的电压值的:
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(retkeyup_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;}
所以当recovery按键按下的时候,通过iio_read_channel_processed()函数获取到的电压值如果与设备树配置相符合的话,就会触发按键上报事件,而用户层会收到事件,从屏幕可以看到有 ”音量+“ 的动作。
获取所有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电压转换后的数字量
会打印 : 0; // 表示recovery 按键按下,对应 0.17v模拟量 当无动作的时候 会打印 :1020; //表示按键抬起,对应 1.8v 模拟量
iio操作说明 获取 ad 通道 structiio_channel*chan;//定义iio通道结构体chan=iio_channel_get(&pdev->dev,xxx);//获取iio通道结构体
注:iio_channel_get 通过 probe 函数传进来的参数 pdev 获取 iio 通道结构体,probe 函数如下:
读取 ad 采集到的原始数据 intval,ret;ret=iio_read_channel_raw(chan,&val);
调用 iio_read_channel_raw 函数读取 ad 采集的原始数据并存入 val 中。
计算采集到的电压 使用标准电压将 ad 转换的值转换为用户所需要的电压值。其计算公式如下:
vref/(2^n-1)=vresult/raw
注:
vref 为标准电压
n 为 ad 转换的位数
vresult 为用户所需要的采集电压
raw 为 ad 采集的原始数据
例如,标准电压为 1.8v,ad 采集位数为 10 位,ad 采集到的原始数据为 568,则:
vresult=(1800mv*568)/1023;
iio接口说明 structiio_channel*iio_channel_get(structdevice*dev,constchar*consumer_channel);
功能:获取 iio 通道描述
参数:
dev: 使用该通道的设备描述指针
consumer_channel: 该设备所使用的 iio 通道描述指针
voidiio_channel_release(structiio_channel*chan);
功能:释放 iio_channel_get 函数获取到的通道
参数:
chan:要被释放的通道描述指针
intiio_read_channel_raw(structiio_channel*chan,int*val);
功能:读取 chan 通道 ad 采集的原始数据。
参数:
chan:要读取的采集通道指针
val:存放读取结果的指针
faqs 为何按上面的步骤申请saradc,会出现申请报错的情况? 驱动需要获取adc通道来使用时,需要对驱动的加载时间进行控制,必须要在saradc初始化之后。saradc是使用module_platform_driver()进行平台设备驱动注册,最终调用的是module_init()。所以用户的驱动加载函数只需使用比module_init()优先级低的,例如:late_initcall(),就能保证驱动的加载的时间比saradc初始化时间晚,可避免出错。
gpio 使用 简介 gpio, 全称 general-purpose input/output(通用输入输出),是一种软件运行期间能够动态配置和控制的通用引脚。 px30有4组gpio bank:gpio0~gpio3,每组又以 a0~a7, b0~b7, c0~c7, d0~d7 作为编号区分(gpio0在pd_pmu子系统中,gpio1/gpio2/gpio3在pd_bus子系统中)。 所有的gpio在上电后的初始状态都是输入模式,可以通过软件设为上拉或下拉,也可以设置为中断脚,驱动强度都是可编程的。每个 gpio 口除了通用输入输出功能外,还可能有其它复用功能,例如:
gpio0_c2 可复用为 i2c1_scl端口
gpio0_c3 可复用为 i2c1_sda端口
每个 gpio 口的驱动电流、上下拉和重置后的初始状态都不尽相同,详细情况请参考《px30 规格书》中的 “chapter 21 gpio” 一章。 px30 的 gpio 驱动是在以下 pinctrl 文件中实现的:
kernel/drivers/pinctrl/pinctrl-rockchip.c
其核心是填充 gpio bank 的方法和参数,并调用 gpiochip_add 注册到内核中。
例子 dts配置 本文以px30的gslx680外设(基于i2c通信的触摸屏)为例,讲述 gpio的输入输出,中断,复用功能的使用,该驱动源码在sdk的路径为:
kernel/drivers/input/touchscreen/gslx680_firefly.c
以下就以该驱动为例介绍gpio的操作。
本例子所需添加的dts资源如下所示:
kernel/arch/arm64/boot/dts/rockchip/px30-firefly-demo.dtsigslx680:gslx680@41{compatible=gslx680;reg=;screen_max_x=;screen_max_y=;touch-gpio=;reset-gpio=;flip-x=;flip-y=;swap-xy=;gsl,fw=;};
kernel/arch/arm64/boot/dts/rockchip/px30.dtsii2c1:i2c@ff190000{compatible=rockchip,rk3399-i2c;reg=;clocks=,;clock-names=i2c,pclk;interrupts=;pinctrl-names=default,gpio(此gpio字段源码未添加);pinctrl-0=;pinctrl-1=;//此处源码未添加#address-cells = ;#size-cells = ;status=disabled;};
输入输出引脚配置 这里使用的是gslx680外设的reset(复位)引脚来讲述gpio的输入输出操作。 在dts配置如下资源:
reset-gpio=;
aio-px30jd4的dts对引脚的描述与firefly-rk3288有所区别,gpio0_b4被描述为:,这里的12来源于:8+4=12,其中8是因为gpio0_b4是属于gpio0的b组,如果是a组的话则为0,如果是c组则为16,如果是d组则为24,以此递推,而4是因为b4后面的4。gpio_active_high表示高电平有效,如果想要低电平有效,可以改为:gpio_active_low,这个属性将被驱动所读取。
中断引脚配置 这里使用的是gslx680外设的irq(中断) 引脚来讲述gpio的中断功能 在dts中配置如下资源:
touch-gpio=;
其中 gpio0 5 的意思是使用gpio0_a5 为中断引脚,irq_type_level_low意思是该引脚低电平(下降沿)的时候触发中断,跳到中断函数执行,中断的触发类型还可以配置如下:
irq_type_none //默认值,无定义中断触发类型 irq_type_edge_rising //上升沿触发 irq_type_edge_falling //下降沿触发 irq_type_edge_both //上升沿和下降沿都触发 irq_type_level_high //高电平触发 irq_type_level_low //低电平触发
复用功能引脚配置 查看芯片的数据手册,可以知道:
pad# func0 func1i2c1_sda/gpio0_c2gpio0c2i2c1_scli2c1_scl/gpio0_c3gpio0c3i2c1_sda
在上面i2c1的dts的配置中,主要有以下关键的描述
pinctrl-names 定义了状态名称列表: default (i2c 功能) 和 gpio 两种状态。
pinctrl-0 定义了状态 0 (即 default)时需要设置的 pinctrl: &i2c4_xfer
pinctrl-1 定义了状态 1 (即 gpio)时需要设置的 pinctrl: &i2c4_gpio
由于在i2c1的dts上gpio的字段属性没有添加,所以默认该两个引脚设置为i2c复用功能,其中pinctrl的描述可以在kernel/arch/arm64/boot/dts/rockchip/px30.dtsi 找到 :
pinctrl:pinctrl{compatible=rockchip,px30-pinctrl;rockchip,grf=;rockchip,pmu=;#address-cells = ;#size-cells = ;ranges;............i2c1{i2c1_xfer:i2c1-xfer{rockchip,pins=,;};/*此段源码未添加i2c1-gpio:i2c1-gpio{rockchip,pins=,;};*/};......}
其中 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
在复用时,如果选择了 “default” (即 i2c 功能),系统会应用 i2c1_xfer 这个 pinctrl,最终将 gpio0_c2 和 gpio0_c3 两个针脚切换成对应的 i2c 功能;而如果选择了 “gpio” ,系统会应用 i2c1_gpio 这个 pinctrl,将 gpio0_c2 和 gpio0_c3 两个针脚还原为 gpio 功能。
由于px30的i2c都是默认复用的,所以在源sdk的px30.dtsi中并没有加上gpio的选择,所以,在i2c总线驱动中:kernel/drivers/i2c/busses/i2c-rk3x.c,并没有加上切换复用功能的源码
如需了解i2c总线驱动是如何切换复用功能的,可以参考源码sdk中的rockchip的官方例子:kernel/drivers/i2c/busses/i2c-rockchip.c 中的rockchip_i2c_probe()函数。
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); // ... }
首先是调用 of_get_gpio 取出设备树中 i2c1 结点的 gpios 属于所定义的两个 gpio:
gpios=,;
然后是调用 devm_gpio_request 来申请 gpio,接着是调用 pinctrl_lookup_state 来查找 “gpio” 状态,而默认状态 “default” 已经由框架保存到 i2c->dev-pins->default_state 中了。最后调用 pinctrl_select_state 来选择是 “default” 还是 “gpio” 功能。
下面是常用的gpio复用 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 驱动解析之输入输出,中断 以下是对px30源sdk中gslx680外设驱动中gsl_ts_probe()函数,gslx680_init()函数,static irqreturn_t gsl_ts_irq()进行部分的解析,用户可以从中了解gpio的输入输出,中断功能的使用,而用于复用功能的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; //设备节点结构体 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); //读取设备树 ts->wake_pin=of_get_named_gpio_flags(np, reset-gpio, 0, &wake_flags); //读取设备树 /*申请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(); //对复位引脚与中断引脚进行初始化操作 ...... /*申请中断irq号,绑定中断函数*/ 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 从设备树中读取 “reset-gpio” 和 “touch-gpio” 的 gpio 配置编号和标志,gpio_is_valid 判断该 gpio 编号是否有效,devm_gpio_request_one则申请占用该 gpio。如果初始化过程出错,会跳到dev_err()函数进行报错与gpio资源释放处理。
调用gpio_to_irq把gpio的pin值转换为相应的irq值,调用devm_request_threaded_irq申请中断,如果失败会goto到标签error_req_irq_fail进行错误处理,gpio资源的释放,该函数中ts->irq是要申请的硬件中断号,gsl_ts_irq是中断函数,irq_flags | irqf_oneshot是中断标志位, client->name是设备驱动程序名称,ts是该设备的device结构体,在注册共享中断时会用到。
在gsl_ts_probe()中,会在上电的时候,对复位引脚以及中断引脚进行初始化操作:
static int gslx680_init(void) { gpio_direction_output(g_wake_pin, 0); //设置该引脚为输出模式,输出0 msleep(20); gpio_set_value(g_wake_pin,1); //设置引脚的输出值为1; msleep(20); gpio_set_value(g_irq_pin,1); //设置中断引脚的初始值为 1 msleep(20); return 0; }
由上面的步骤可知晓,在设备上电的时候,可以用示波器测试出,该reset引脚会出现一个复位的操作。
而对于中断而言,在用户在进入系统之后,点击触摸屏,会把中断引脚拉低,由上面的devm_request_threaded_irq()函数可知,该中断在触发的时候,会跳到static irqreturn_t gsl_ts_irq()中断函数中去执行:
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); //在进入中断函数后,会打印这一句话,但源码未添加,用户可以自行添加来验证。 return irq_handled; }
上面这个中断函数主要是做了一些键值的上报,由于本文未涉及input输入子系统,所以不在此处讲述。
下面是常用的 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);
调试方法 io指令 gpio调试有一个很好用的工具,那就是io指令,android系统默认已经内置了io指令,使用io指令可以实时读取或写入每个io口的状态,这里简单介绍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当前各引脚值的情况
从主控的datasheet查到gpio0_iomux对应寄存器基地址为:ff040000
# io -4 -r 0xff040000ff040000:00003807
如果想改变gpio的配置值,可以使用以下指令设置:
# io -4 -w 0xff040000 0xxxxxxxxx(你想要设置的值,例如0x00001101)
gpio调试接口 debugfs文件系统目的是为开发人员提供更多内核数据,方便调试。 这里gpio的调试也可以用debugfs文件系统,获得更多的内核信息。 gpio在debugfs文件系统中的接口为 /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:
从读取到的信息中可以知道,内核把gpio当前的状态都列出来了,以gpio0组为例,gpio-5(gpio0_a5)作为gslx680 模块的中断引脚,设置输入,输出高电平。
faqs q1: 如何将pin的mux值切换为一般的gpio? a1: 当使用gpio request时候,会将该pin的mux值强制切换为gpio,所以使用该pin脚为gpio功能的时候确保该pin脚没有被其他模块所使用。
q2: 为什么我用io指令读出来的值都是0x00000000? a2: 如果用io命令读某个gpio的寄存器,读出来的值异常,如 0x00000000或0xffffffff等,请确认该gpio的clk是不是被关了,gpio的clk是由cru控制,可以通过读取datasheet下面cru_clkgate_con* 寄存器来查到clk是否开启,如果没有开启可以用io命令设置对应的寄存器,从而打开对应的clk,打开clk之后应该就可以读到正确的寄存器值了。
q3: 测量到pin脚的电压不对应该怎么查? a3: 测量该pin脚的电压不对时,如果排除了外部因素,可以确认下该pin所在的io电压源是否正确,以及io-domain配置是否正确。
q4: gpio_set_value()与gpio_direction_output()有什么区别? a4: 如果使用该gpio时,不会动态的切换输入输出,建议在开始时就设置好gpio 输出方向,后面拉高拉低时使用gpio_set_value()接口,而不建议使用gpio_direction_output(), 因为gpio_direction_output接口里面有mutex锁,对中断上下文调用会有错误异常,且相比 gpio_set_value,gpio_direction_output 所做事情更多,浪费。
i2c 使用 简介 aio-px30-jd4 开发板上有 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复用为其他功能gpio1_b5/i2c_scl
本文主要描述如何在该开发板上配置 i2c。
配置 i2c 可分为两大步骤:
定义和注册 i2c 设备
定义和注册 i2c 驱动
下面以配置 gsl3680 (触摸屏)为例。
定义和注册 i2c 设备 在注册i2c设备时,需要结构体 i2c_client 来描述 i2c 设备。然而在标准linux中,用户只需要提供相应的 i2c 设备信息,linux就会根据所提供的信息构造 i2c_client 结构体。
用户所提供的 i2c 设备信息以节点的形式写到 dts 文件中,路径为 kernel/arch/arm64/boot/dts/rockchip/px30-firefly-aiojd4-lvds.dts ,如下所示:
&i2c2{status=okay;......gslx680:gslx680@41{compatible=gslx680;reg=;screen_max_x=;screen_max_y=;touch-gpio=;reset-gpio=;flip-x=;flip-y=;swap-xy=;gsl,fw=;};......};
定义和注册 i2c 驱动 该驱动的路径为:kernel/drivers/input/touchscreen/gslx680_firefly.c
定义 i2c 驱动 在定义 i2c 驱动之前,用户首先要定义变量 of_device_id 和 i2c_device_id 。
of_device_id 用于在驱动中调用dts文件中定义的设备信息,其定义如下所示:
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指示该驱动所支持的设备。
注册 i2c 驱动 使用i2c_add_driver函数注册 i2c 驱动。
i2c_add_driver(&gsl_ts_driver);
在调用 i2c_add_driver 注册 i2c 驱动时,会遍历 i2c 设备,如果该驱动支持所遍历到的设备(即id_table的值与设备树的compatible属性值相同),则会调用该驱动的 probe 函数。
通过 i2c 收发数据 在注册好 i2c 驱动后,即可进行 i2c 通讯。
在该驱动的gsl_ts_probe()函数中,会对gslx680的ic进行初始化,而在初始化的代码中,会对主从设备的通讯进行一个测试
gsl_ts_probe()->init_chip()->test_i2c().
而在 test_i2c()这个函数中,会存在gsl_ts_read(),gsl_ts_write()两个gslx680驱动自己封装的主机发送和主机接受函数,其内部真正调用的是linux内核提供的i2c通讯函数。
向从机发送信息:
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()函数来进行接受或者发送的时候,也是调用i2c_transfer()这个函数来处理一个消息结构体(i2c_msg),而对于一些处理信息比较复杂的i2c设备,可以直接调用i2c_transfer()来处理信息,不过要自己构造 i2c_msg 结构体。
struct i2c_msg { __u16 addr; //iic从设备地址 __u16 flags;//操作标志位,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; //传输的数据长度,字节为单位 __u8 *buf; //存放read或write的数据的buffer };
faqs q1: 通信失败,出现这种log:”timeout, ipd: 0x00, state: 1”该如何调试? a1: 请检查硬件上拉是否给电。
q2: 调用i2c_transfer返回值为-6? a2: 返回值为-6表示为nack错误,即对方设备无应答响应,这种情况一般为外设的问题,常见的有以下几种情况:
i2c地址错误,解决方法是测量i2c波形,确认是否i2c 设备地址错误;
i2c slave 设备不处于正常工作状态,比如未给电,错误的上电时序等;
时序不符合 i2c slave设备所要求也会产生nack信号。
q3: 当外设对于读时序要求中间是stop信号不是repeat start信号的时候,该如何处理? a3: 这时需要调用两次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 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 adapter->dev, i2c read failed\n); else memcpy(data, buffer, data_len); kfree(buffer); return ret; }
ir 使用 红外遥控配置 aio-px30-jd4 开发板上使用红外收发传感器 ir (麦克风和i2c0之间)实现遥控功能,在ir接口处接上红外接收器。本文主要描述在开发板上如何配置红外遥控器。
其配置步骤可分为两个部分:
修改内核驱动:内核空间修改,linux 和 android 都要修改这部分的内容。
修改键值映射:用户空间修改(仅限 android 系统)。
配置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=;handle_cpu_id=;remote_support_psci=;ir_key1{rockchip,usercode=;rockchip,key_table=,,,,,,,,,,,;};};
注1:第一列为键值,第二列为要响应的按键码。 注2:由于uart3的rx与ir复用了,所以要使用ir功能,就需要在设备树上关闭uart3。
&uart3{......status=disabled......};
内核驱动 在 linux 内核中,ir 驱动仅支持 nec 编码格式。以下是在内核中配置红外遥控的方法。
所涉及到的文件
drivers/input/remotectl/rockchip_pwm_remotectl.c
如何获取用户码和ir 键值 在 remotectl_do_something 函数中获取用户码和键值:
casermc_usercode:{//ddata->scandataperiodscandata|=(0x01count++;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() 函数打印用户码。
使用下面命令可以使能dbg_code打印:
echo1>/sys/module/rockchip_pwm_remotectl/parameters/code_print
将 ir 驱动编译进内核 将 ir 驱动编译进内核的步骤如下所示:
(1)、向配置文件 drivers/input/remotectl/kconfig 中添加如下配置:
configrockchip_remotectl_pwmboolrockchip remoctrl pwm capturedefaultn
(2)、修改 drivers/input/remotectl 路径下的 makefile,添加如下编译选项:
obj-$(config_rockchip_remotectl_pwm) += rockchip_pwm_remotectl.o
(3)、在 kernel 路径下使用 make menuconfig ,按照如下方法将ir驱动选中。
devicedrivers--->inputdevicesupport----->[*]rockchipremotectl---------->[*]rockchipremoctrlpwmcapture
保存后,执行 make 命令即可将该驱动编进内核。
android 键值映射 文件 /system/usr/keylayout/ff200030_pwm.kl 用于将 linux 层获取的键值映射到 android 上对应的键值。用户可以添加或者修改该文件的内容以实现不同的键值映射。
该文件内容如下所示:
key28enterkey116powerkey158backkey139menukey217searchkey232dpad_centerkey108dpad_downkey103dpad_upkey102homekey105dpad_leftkey106dpad_rightkey115volume_upkey114volume_downkey143notificationkey113volume_mutekey388tv_keymouse_mode_switch
注:通过 adb 修改该文件重启后即可生效。
ir 触发 下图是当红外遥控器按钮按下的时候,所产生的波形,主要由head,control,information,signed free这四部分组成,具体可以参考rc6 protocol。
实物连接图
lcd使用 简介 aio-px30-jd4开发板默认外置支持了一个lcd屏接口,为lvds,另外板子也支持mipi屏幕,但需要注意的是mipi和lvds是复用的,使用lvds之后不能使用mipi,接口如下图:
config配置 由于aio-px30-jd4默认使用的是lvds屏幕,同时在默认的配置文件kernel/arch/arm64/configs/firefly_defconfig已经把lcd相关的配置设置好了,如果自己做了修改,请注意把以下配置加上:
config_rockchip_dw_mipi_dsi=y......config_rockchip_lvds=y
dts配置 dsi_phy配置 aio-px30-jd4中关于lvds(mipi) dsi_phy的dts配置在:kernel/arch/arm64/boot/dts/rockchip/px30.dtsi中,从该文件我们可以看到:
......display_subsystem:display-subsystem{compatible=rockchip,display-subsystem;ports=,;status=disabled;};......lvds:lvds@ff2e0000{compatible=rockchip,px30-lvds;reg=,;clocks=,;clock-names=pclk_lvds,pclk_lvds_ctl;power-domains=;rockchip,grf=;status=disabled;ports{#address-cells = ;#size-cells = ;port@0{reg=;#address-cells = ;#size-cells = ;lvds_in_vopb:endpoint@0{reg=;remote-endpoint=;};lvds_in_vopl:endpoint@1{reg=;remote-endpoint=;};};};};......
而在kernel/arch/arm64/boot/dts/rockchip/px30-firefly-aiojd4-lvds.dts 也存在对以上dts进行引用配置
&display_subsystem{status=okay;//turnonthedisplaysubsystem};&lvds{status=okay;//openlvdsfunctionports{port@1{reg=;lvds_out_panel:endpoint{remote-endpoint=;};};};};&lvds_in_vopl{status=disabled;};&lvds_in_vopb{status=okay;};&route_lvds{status=okay;};
backlight配置 aio-px30-jd4开发板外置了一个背光接口用来控制屏幕背光,如下图所示:
主要有背光电源引脚以及控制亮度引脚,dts:kernel/arch/arm64/boot/dts/rockchip/px30-firefly-aiojd4-lvds.dts配置如下
......backlight:backlight{status=okay;compatible=pwm-backlight;enable-gpios=;pwms=;brightness-levels=;default-brightness-level=;};......
enable-gpios 属性为背光的电源控制引脚。
pwms属性:配置pwm,可用来改变输出占空比(范例里面默认使用pwm0,50000ns是周期(20 khz)。
brightness-levels属性:配置背光亮度数组,最大值为255,配置暗区和亮区,并把亮区数组做255的比例调节。比如范例中暗区是255-221,亮区是220-0。 由于px30使用200以上的level屏幕就会过暗,所以默认最大值为200。
default-brightness-level属性:开机时默认背光亮度,范围为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{clock-frequency=;hactive=;vactive=;hfront-porch=;hsync-len=;hback-porch=;vfront-porch=;vsync-len=;vback-porch=;hsync-active=;vsync-active=;de-active=;pixelclk-active=;};};......
时序属性参考下图:
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);}
详细流程说明可参考以下附件: rockchip drm panel porting guide.pdf
led 使用 前言 aio-px30-jd4 开发板上有 2 个 led 灯,如下表所示:
ledgpioref.gpionumberbluegpio1_b545yellowgpio1_b444
以设备的方式控制 led可通过使用 led 设备子系统或者直接操作 gpio 控制该 led。
标准的 linux 专门为 led 设备定义了 led 子系统。 在 aio-px30-jd4 开发板中的两个 led 均以设备的形式被定义。
用户可以通过 /sys/class/leds/ 目录控制这两个 led。
开发板上的 led 的默认状态为:
blue: 系统上电时打开
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
更详细的说明请参考 kernel/documentation/leds/leds-class.txt ,有内核对led相关功能的支持的描述。
首先我们需要知道定义多少个led,同时对应的led的属性是什么。
在 kernel/arch/arm64/boot/dts/rockchip/px30-firefly-aiojd4-lvds.dts 文件中定义led节点,具体定义如下:
leds{compatible=gpio-leds;power_led::power{label=firefly:blue:power;linux,default-trigger=ir-power-click;default-state=on;gpios=;pinctrl-names=default;pinctrl-0=;};user_led:user{label=firefly:yellow:user;linux,default-trigger=ir-user-click;default-state=off;gpios=;pinctrl-names=default;pinctrl-0=;};};
注意:compatible 的值要跟 drivers/leds/leds-gpio.c 中的 .compatible 的值要保持一致。
simple trigger led 这是使用简单的触发方式控制来led,如下就默认打开黄灯:
(1)定义 led 触发器 在kernel/drivers/leds/trigger/led-firefly-demo.c 文件中有如下添加
define_led_trigger(ledtrig_default_control);
(2)注册该触发器
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,//关闭ledled_half=127,//led设置为一半的亮度led_full=255,//led设置为全部的亮度};
(4)打开led demo
led-firefly-demo默认没有打开,如果需要的话可以使用以下补丁打开demo驱动:
---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复杂一点的例子,timer trigger 就是让led达到不断亮灭的效果
我们需要在内核把timer trigger配置上
在 kernel 路径下使用 make menuconfig ,按照如下方法将timer trigger驱动选中。
devicedrivers--->ledsupport--->ledtriggersupport--->ledtimertrigger
保存配置并编译内核,把kernel.img 烧到aio-px30-jd4板子上 我们可以使用串口输入命令,就可以看到蓝灯不停的间隔闪烁
echotimer>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 开发板带有一个mipi camera,为mipi_csi,mipi最高支持 3264x2448 pixels拍照。
本文以 ov13850 摄像头为例,讲解在该开发板上的配置过程。
接口效果图
dts配置 kernel/arch/arm64/boot/dts/rockchip/px30.dtsi:
rk_isp:rk_isp@ff4a0000{compatible=rockchip,px30-isp,rockchip,isp;reg=;interrupts=;clocks=,,,,,,,;clock-names=aclk_isp,hclk_isp,clk_isp,clk_isp_jpe,pclkin_isp,clk_cif_pll,clk_cif_out,pclk_dphyrx;resets=,;reset-names=rst_isp,rst_mipicsiphy;power-domains=;pinctrl-names=default,isp_dvp8bit2,isp_dvp10bit,isp_dvp12bit;pinctrl-0=;pinctrl-1=;pinctrl-2=;pinctrl-3=;rockchip,isp,mipiphy=;rockchip,isp,csiphy,reg=;rockchip,grf=;rockchip,cru=;rockchip,isp,iommu-enable=;iommus=;status=disabled;};
驱动说明 与摄像头相关的代码目录如下:
android: `- hardware/rockchip/camera/ |- camerahal // 摄像头的 hal 源码 `- siliconimage // isp 库,包括所有支持模组的驱动源码 `- isi/drv/ov13850 // ov13850 模组的驱动源码 `- calib/ov13850.xml // ov13850 模组的调校参数 `- hardware/rockchip/camera/config/ |- cam_board_rk3326.xml // 摄像头的参数设置 kernel: |- kernel/drivers/media/video/rk_camsys // camsys 驱动源码 `- kernel/include/media/camsys_head.h
配置原理 设置摄像头相关的引脚和时钟,即可完成配置过程。
从以下摄像头接口原理图可知,需要配置的引脚有:cif_pwr、dvp_pwr和mipi_rst。
mipi接口
dvp_pwr 对应 px30 的 gpio1_b7;
cif_pwr 对应 px30 的 gpio1_b6;
mipi_rst对应 px30 的 gpio2_b3;
在开发板中,这三个引脚都是在 cam_board_rk3326.xml 中设置。
配置步骤 配置 android 修改hardware/rockchip/camera/config/cam_board_rk3326.xml 来注册摄像头:

精彩回顾 意瑞亮相2022慕尼黑华南电子展
Atmel:全新的运营模式助力创客创新
联想玛雅之光键鼠套装评测 拯救小白用户的专业级电竞键鼠
可燃气体检测仪在使用中有哪些注意事项?-欧森杰
物联网在军事领域取得了哪些研究成果?
fireflyCORE-PX30-JD4驱动开发介绍
双光路激光焊接机的工艺特点
配置比较高的蓝牙耳机有哪些?超长待机蓝牙耳机排行榜
与快充相关的AC/DC功率变换关键技术探讨
怎样使Garmin车辆电源线重新工作
运营商携号转网工作错综复杂实施携号转网并非易事
MEMS传感器内置的MLC与FSM有什么差别?
一文了解PCB电源如何布置
美格智能亮相深圳电力展 “国产芯”通信模组助力智能抄表智慧连接
锂电池电解液技术升级 突破材料与技术困局
Arm携手Cadence加速AI时代芯片开发
康佳半导体产业园项目落户合肥
千视NDI直播间方案助力吉利集团打造直播矩阵
互联网+如何创造更美好的生活
超声波塑料焊接机的应用领域