A 平台ADC 按键配置

不同芯片厂商配置keypad 的方式不一样,基本原理一样“将sar 口电压值分成几个区间,每个区间映射一个KeyCode,然后注册一个input 设备,将keycode 抛上去”,上层应用通过捕获keycode 做逻辑处理。

我们先看一下电路连接
在这里插入图片描述
pin E15 作为SARADC_CH0 接收按键板产生的电压变化,根据配置区间映射上抛keycode。

adc_keypad 示例

Required properties:
- compatible: Must be "amlogic, adc_keypad".
- key_name: Specify the name of different key.
- key_num:  Specify the quantity of the keys.
- key_code: Set the function code corresponding to different key.
- key_chan: Set the channel to detect the key, can be set from 0 to 7.
- key_val: The adc sampling value of different key, can be set from 0 to 1023.
- key_tolerance: tolerance of the adc sampling value, recommend maximum value of 40.

Optional properties:
- status: Shall be "ok" or "okay" if enabled or "disabled" if disabled.
          Default is "ok".

Example:
	adc_keypad{
			compatible = "amlogic, adc_keypad";
			status = "okay";
			key_name = "left", "right","up", "down", "enter";
			key_num = <5>;
			key_code = <106 105 103 108 28>;
			key_chan = <2 2 3 3 3>;
			key_val =  <516 253 516 750 0>; //voltage=0/252/478/692/824mV, val=voltage/1800mV*1023
			key_tolerance = <40 40 40 40 40>;
   };

定义了key_name,key_code,key_val,key_num分别表示按键名称,按键的码值,按键定义的电压值,按键的数量

定义adc_keypad

arch\arm64\boot\dts\amlogic\axg_s420.dts

	adc_keypad {
		compatible = "amlogic, adc_keypad";
		status = "okay";
		key_name = "power", "vol-", "vol+", "wifi", "<<", ">>";   // 按键名称
		key_num = <6>;   //   按键个数
		io-channels = <&saradc SARADC_CH0>;
		io-channel-names = "key-chan-0";
		key_chan = <SARADC_CH0 SARADC_CH0 SARADC_CH0
					SARADC_CH0 SARADC_CH0 SARADC_CH0>;  // 表示每个按键对应的SAR通道
		key_code = <116 114 115 139 105 106>;  //  键值
		key_val = <0 143 266 389 512 635>; //val=voltage/1800mV*1023  电压
		key_tolerance = <40 40 40 40 40 40>;
	};

SARADC_CH0 值SAR 通道,在amlogic-saradc.h中

#ifndef _DT_BINDINGS_IIO_ADC_AMLOGIC_H
#define _DT_BINDINGS_IIO_ADC_AMLOGIC_H

#define SARADC_CH0	0
#define SARADC_CH1	1
#define SARADC_CH2	2
#define SARADC_CH3	3
#define SARADC_CH4	4
#define SARADC_CH5	5
#define SARADC_CH6	6
#define SARADC_CH7	7

#define SARADC_CH_NUM 8
#endif

io-channels 引用的saradc 在mesonaxg.dtsi 中定义

	saradc:saradc {
		compatible = "amlogic,meson-axg-saradc";
		status = "okay";
		#io-channel-cells = <1>;
		clocks = <&xtal>, <&clkc CLKID_SARADC_GATE>;
		clock-names = "xtal", "saradc_clk";
		interrupts = <GIC_SPI 73 IRQ_TYPE_EDGE_RISING>;
		reg = <0x0 0xff809000 0x0 0x38>;
	};

驱动解析aml_keypad 属性

meson_adc_kp_get_devtree_pdata @drivers\amlogic\input\keyboard\adc_keypad.c
完成key_name,key_code,key_chan,key_val,key_tolerance等设备属性解析

static int meson_adc_kp_get_devtree_pdata(struct platform_device *pdev,
			struct meson_adc_kp *kp)
{
	int ret;
	int count;
	int value;
	int state = 0;
	unsigned char cnt;
	const char *uname;
	unsigned int key_num;
	struct adc_key *key;
	struct of_phandle_args chanspec;

	if (!pdev->dev.of_node) {
		dev_err(&pdev->dev, "failed to get device node\n");
		return -EINVAL;
	}

	count = of_property_count_strings(pdev->dev.of_node,
		"io-channel-names");
	if (count < 0) {
		dev_err(&pdev->dev, "failed to get io-channel-names");
		return -ENODATA;
	}

	ret = of_property_read_u32(pdev->dev.of_node, "poll-interval", &value);
	if (ret)
		kp->poll_period = POLL_INTERVAL_DEFAULT;
	else
		kp->poll_period = value;

	for (cnt = 0; cnt < count; cnt++) {
		ret = of_parse_phandle_with_args(pdev->dev.of_node,
			"io-channels", "#io-channel-cells", cnt, &chanspec);
		if (ret)
			return ret;

		if (!chanspec.args_count)
			return -EINVAL;

		if (chanspec.args[0] >= SARADC_CH_NUM) {
			dev_err(&pdev->dev, "invalid channel index[%u]\n",
					chanspec.args[0]);
			return -EINVAL;
		}

		ret = of_property_read_string_index(pdev->dev.of_node,
				"io-channel-names", cnt, &uname);
		if (ret < 0) {
			dev_err(&pdev->dev, "invalid channel name index[%d]\n",
					cnt);
			return -EINVAL;
		}

		kp->pchan[chanspec.args[0]] = devm_iio_channel_get(&pdev->dev,
				uname);
		if (IS_ERR(kp->pchan[chanspec.args[0]]))
			return PTR_ERR(kp->pchan[chanspec.args[0]]);
	}

	ret = of_property_read_u32(pdev->dev.of_node, "key_num", &key_num);
	if (ret) {
		dev_err(&pdev->dev, "failed to get key_num!\n");
		return -EINVAL;
	}

	for (cnt = 0; cnt < key_num; cnt++) {
		key = kzalloc(sizeof(struct adc_key), GFP_KERNEL);
		if (!key) {
			dev_err(&pdev->dev, "alloc mem failed!\n");
			return -ENOMEM;
		}

		ret = of_property_read_string_index(pdev->dev.of_node,
			 "key_name", cnt, &uname);
		if (ret < 0) {
			dev_err(&pdev->dev, "invalid key name index[%d]\n",
				cnt);
			state = -EINVAL;
			goto err;
		}
		strncpy(key->name, uname, MAX_NAME_LEN);

		ret = of_property_read_u32_index(pdev->dev.of_node,
			"key_code", cnt, &key->code);
		if (ret < 0) {
			dev_err(&pdev->dev, "invalid key code index[%d]\n",
				cnt);
			state = -EINVAL;
			goto err;
		}

		ret = of_property_read_u32_index(pdev->dev.of_node,
			"key_chan", cnt, &key->chan);
		if (ret < 0) {
			dev_err(&pdev->dev, "invalid key chan index[%d]\n",
				cnt);
			state = -EINVAL;
			goto err;
		}

		if (!kp->pchan[key->chan]) {
			dev_err(&pdev->dev, "invalid channel[%u], please enable it first by DTS\n",
					key->chan);
			state = -EINVAL;
			goto err;
		}

		ret = of_property_read_u32_index(pdev->dev.of_node,
			"key_val", cnt, &key->value);
		if (ret < 0) {
			dev_err(&pdev->dev, "invalid key value index[%d]\n",
				cnt);
			state = -EINVAL;
			goto err;
		}

		ret = of_property_read_u32_index(pdev->dev.of_node,
			"key_tolerance", cnt, &key->tolerance);
		if (ret < 0) {
			dev_err(&pdev->dev, "invalid key tolerance index[%d]\n",
				cnt);
			state = -EINVAL;
			goto err;
		}
		list_add_tail(&key->list, &kp->adckey_head);
	}
	meson_adc_kp_get_valid_chan(kp);
	return 0;
err:
	kfree(key);
	return state;

}

input poll device 注册

static int meson_adc_kp_probe(struct platform_device *pdev)
{
	......

	// send data to bl301 for machine resume
	send_data_to_bl301();

	// lock/unlock keypad
	kernel_keypad_enable_mode_enable();
	
	......

	// parse the device tree
	ret = meson_adc_kp_get_devtree_pdata(pdev, kp);
	if (ret)
		goto err;

	/*alloc input poll device*/
	kp->poll_dev = devm_input_allocate_polled_device(&pdev->dev);
	if (!kp->poll_dev) {
		dev_err(&pdev->dev, "alloc input poll device failed!\n");
		ret = -ENOMEM;
		goto err;
	}

	......

	meson_adc_kp_init_keybit(kp);

	input->name = "adc_keypad";
	input->phys = "adc_keypad/input0";
	input->dev.parent = &pdev->dev;

	input->id.bustype = BUS_ISA;
	input->id.vendor = 0x0001;
	input->id.product = 0x0001;  // set vendor id
	input->id.version = 0x0100;  // set product id

	input->rep[REP_DELAY] = 0xffffffff;
	input->rep[REP_PERIOD] = 0xffffffff;

	input->keycodesize = sizeof(unsigned short);
	input->keycodemax = 0x1ff;

	......

	/*init class*/
	......

	/*register input poll device*/
	ret = input_register_polled_device(kp->poll_dev);
	if (ret) {
		dev_err(&pdev->dev,
			 "unable to register keypad input poll device.\n");
		goto err1;
	}

	return ret;

err1:
	class_unregister(&kp->kp_class);
err:
	meson_adc_kp_list_free(kp);
	kfree(kp);
	return ret;
}

完成input poll 设备的注册之后,就会生成设备节点。

aml_keypad 属性查看

通过命令cat /sys/class/adc_keypad/table,会调用table_show函数

static ssize_t table_show(struct class *cls, struct class_attribute *attr,
			char *buf)
{
	struct meson_adc_kp *kp = container_of(cls,
					struct meson_adc_kp, kp_class);
	struct adc_key *key;
	unsigned char key_num = 1;
	int len = 0;

	mutex_lock(&kp->kp_lock);
	list_for_each_entry(key, &kp->adckey_head, list) {
		len += sprintf(buf+len,
			"[%d]: name=%-21s code=%-5d channel=%-3d value=%-5d tolerance=%-5d\n",
			key_num,
			key->name,
			key->code,
			key->chan,
			key->value,
			key->tolerance);
		key_num++;
	}
	mutex_unlock(&kp->kp_lock);

	return len;
}

执行结果如下:
在这里插入图片描述

adc_keypad 应用

下图展现了整个aml_keypad 的整个按键的流程
在这里插入图片描述

扫描二维码关注公众号,回复: 8876499 查看本文章

SARADC 其它应用

通过SAR口映射HW Ver ,以便兼容不同HW。也可以保护自家产品。

发布了101 篇原创文章 · 获赞 19 · 访问量 33万+

猜你喜欢

转载自blog.csdn.net/kehyuanyu/article/details/100141876
ADC