標準linux4.4--驅動開發(一)ADC驅動編寫

標準linux4.4--驅動開發(一)ADC驅動編寫

簡介

Firefly-RK3308開發板上的 AD 接口有兩種

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

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

內核採用工業 I/O 子系統來控制 ADC,該子系統主要爲 AD 轉換或者 DA 轉換的傳感器設計。 下面以 SAR-ADC 爲例子,介紹 ADC 的基本配置方法。

DTS配置

SAR-ADC 的 DTS 節點在 kernel/arch/arm64/boot/dts/rockchip/rk3308.dtsi 文件中定義,如下所示:

	saradc: saradc@ff1e0000 {
		compatible = "rockchip,rk3308-saradc", "rockchip,rk3399-saradc";
		reg = <0x0 0xff1e0000 0x0 0x100>;
		interrupts = <GIC_SPI 37 IRQ_TYPE_LEVEL_HIGH>;
		#io-channel-cells = <1>;
		clocks = <&cru SCLK_SARADC>, <&cru PCLK_SARADC>;
		clock-names = "saradc", "apb_pclk";
		resets = <&cru SRST_SARADC_P>;
		reset-names = "saradc-apb";
		status = "disabled";
	};

想要使用CPU上的ADC資源,必須要添加DTS配置及設備樹,所以用戶首先需在 DTS 文件中添加 ADC 的資源描述:
這裏我舉2個例子
(1)adc模擬的按鍵驅動(RK適配的)
(2)螢火蟲firefly適配的adc使用demo
接下來看下他們分別的設備樹如何描述
(1)adc模擬的按鍵(簡單邏輯根據adc檢測的電壓的不同,轉換成數字信號範圍去檢測按了哪個按鍵)

adc-keys {
		compatible = "adc-keys";
		io-channels = <&saradc 1>;//這裏申請的是 SARADC 通道1。
		io-channel-names = "buttons";
		poll-interval = <100>;
		keyup-threshold-microvolt = <1800000>;

		esc-key {
			linux,code = <KEY_MICMUTE>;
			label = "micmute";
			press-threshold-microvolt = <1130000>;
		};

		home-key {
			linux,code = <KEY_MODE>;
			label = "mode";
			press-threshold-microvolt = <901000>;
		};

		menu-key {
			linux,code = <KEY_PLAY>;
			label = "play";
			press-threshold-microvolt = <624000>;
		};

		vol-down-key {
			linux,code = <KEY_VOLUMEDOWN>;
			label = "volume down";
			press-threshold-microvolt = <300000>;
		};

		vol-up-key {
			linux,code = <KEY_VOLUMEUP>;
			label = "volume up";
			press-threshold-microvolt = <18000>;
		};
	};

(2)adc-demo

adc_demo: adc_demo{
       status = "disabled";
       compatible = "firefly,rk3399-adc";
       io-channels = <&saradc 3>;//這裏申請的是 SARADC 通道3。
   };

(1)adc按鍵驅動下載鏈接
(2)點擊adc使用demo驅動下載

在驅動文件中關聯DTS的配置

用戶驅動可參考 RK adc Key驅動 :這是一個偵測 模擬出的5個按鍵狀態的驅動。首先在驅動文件中定義 of_device_id 結構體數組:

static const struct of_device_id adc_keys_of_match[] = {
	{ .compatible = "adc-keys", },
	{ }
};

然後將該結構體數組填充到要使用 ADC 的 platform_driver 中:

static struct platform_driver __refdata adc_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);

接着在 probe函數 中對 DTS 所添加的資源進行解析:
簡單邏輯是:獲取道設備樹描述的節點,遍歷節點(有多少個節點都會讀取到),在獲取節點中相應的屬性,分別保存起來。
例句相關部分驅動:(具體可以下載看)
ADC相關函數:


	st = devm_kzalloc(dev, sizeof(*st), GFP_KERNEL);//申請內核空間內核
	if (!st)
		return -ENOMEM;

	st->channel = devm_iio_channel_get(dev, "buttons");//獲取設備IIO通道 與str匹配
	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);//獲取io通道類型
	if (error < 0)
		return error;

	if (type != IIO_VOLTAGE) {
		dev_err(dev, "Incompatible channel type %d\n", type);
		return -EINVAL;
	}

讀取設備樹中的屬性

device_for_each_child_node(dev, child) {//循環去讀子節點按鍵
		if (fwnode_property_read_u32(child, "press-threshold-microvolt",//讀取dtsi中子節點中press-threshold-microvolt屬性
					     &map[i].voltage)) {
			dev_err(dev, "Key with invalid or missing voltage\n");
			fwnode_handle_put(child);
			return -EINVAL;
		}
		map[i].voltage /= 1000;

		if (fwnode_property_read_u32(child, "linux,code",//讀取dtsi中子節點中linux,code屬性
					     &map[i].keycode)) {
			dev_err(dev, "Key with invalid or missing linux,code\n");
			fwnode_handle_put(child);
			return -EINVAL;
		}

		i++;
	}

驅動說明

st->channel = devm_iio_channel_get(dev, "buttons");//獲取設備IIO通道 與str匹配

 iio_get_channel_type(st->channel, &type);//獲取io通道類型

iio_read_channel_processed(st->channel, &value);//讀ADC通道的值 存到value中

ADC驅動用到的函數比較簡單。

FAQs

爲何按上面的步驟申請 SARADC,會出現申請報錯的情況?
驅動需要獲取ADC通道來使用時,需要對驅動的加載時間進行控制,必須要在saradc初始化之後。saradc是使用module_platform_driver()進行平臺設備驅動註冊,最終調用的是module_init()。所以用戶的驅動加載函數只需使用比module_init()優先級低的,例如:late_initcall(),就能保證驅動的加載的時間比saradc初始化時間晚,可避免出錯。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章