Zephyr ADC

设备树编写

在Zephyr中,板载的绝大部分的外设默认情况下都是disabled的,如果需要使用某个功能,就需要在overlay中使能对应的节点,并添加外设的初始化参数,某些情况下可能还需要使用 pinctrl 进行IO复用。

下面是ADC例程中的设备树文件:

/ {
	zephyr,user {
		io-channels =
			<&adc0 0>,
			<&adc1 0>;
	};
};

&adc0 {
	status = "okay";
	#address-cells = <1>;
	#size-cells = <0>;

	channel@0 {
		reg = <0>;
		zephyr,gain = "ADC_GAIN_1_4";
		zephyr,reference = "ADC_REF_INTERNAL";
		zephyr,acquisition-time = <ADC_ACQ_TIME_DEFAULT>;
		zephyr,resolution = <12>;
	};
};

&adc1 {
	status = "okay";
	#address-cells = <1>;
	#size-cells = <0>;

	channel@0 {
		reg = <0>;
		zephyr,gain = "ADC_GAIN_1_4";
		zephyr,reference = "ADC_REF_INTERNAL";
		zephyr,acquisition-time = <ADC_ACQ_TIME_DEFAULT>;
		zephyr,resolution = <12>;
	};
};
  • 上面分别使用了adc0和adc1的0通道作为ADC的输入端口,zephyr,user 是一个特殊的节点,在里面可以存放任意的属性而不需要编写绑定文件,这意味着当你需要用到一些简单的属性时可以把它当作便捷的容器。

  • 以 phandle 作为节点名称时,如果SOC设备树文件中存在该节点,与此处同名的属性会被覆盖,而此处新增的属性则会和将其添加到最后生成的设备树中,如果此处不以 phandle 类型做节点名,而是与SOC中的节点同名,那么此处的节点会替换 SOC设备树文件中的节点。

  • 在Zephyr中,设备树的编译需要与对应的绑定文件相匹配,如果不知道参数ADC参数如何设置,可通过SOC设备树文件中对应节点的 compatible 属性搜索包含该关键字的 yaml 文件,里面提供了对应的模板和使用实例,哪些属性是必须的,哪些属性是可选的。

  • 如果需要修改ADC的通道,需要修改三个地方,第一个是 zephyr,user 下 io-channels 数组中的第二个参数,第二个是channel节点后的地址,第三个是reg中的地址,由于reg是对节点地址信息更详细的描述,所以也必须要一致,如果忘记修改会导致初始化失败。

设备树节点访问

#define DT_ADC_NODE_SPEC_GET(node_id, prop, index) \
	ADC_DT_SPEC_GET_BY_IDX(node_id, index),

static const struct adc_dt_spec adc_channels[] = {
	DT_FOREACH_PROP_ELEM(DT_PATH(zephyr_user), io_channels, DT_ADC_NODE_SPEC_GET)
};
  • ADC_DT_SPEC_GET_BY_IDX 可以获取到ADC设备结构体,设备树中的ADC配置信息,由于io-channels 是一个 phandle 类型的数组,要想获取到所有节点的信息只能通过遍历函数来实现。

  • 将上述宏展开后得到的结果如下:

static const struct adc_dt_spec adc_channels[] = {
	ADC_DT_SPEC_GET_BY_IDX(DT_N_S_zephyr_user, 0),
	ADC_DT_SPEC_GET_BY_IDX(DT_N_S_zephyr_user, 1),
};
  • 除了 ADC_DT_SPEC_GET_BY_IDX ,驱动中还提供了其他的宏用于获取配置,这些宏在使用时都需要将设备树中将对应的属性设置为 phandle 数组,类似 <&adc0, 1> 这样的形式。

    • ADC_DT_SPEC_GET_BY_IDX(node_id, idx)
    • ADC_DT_SPEC_INST_GET_BY_IDX(inst, idx)
    • ADC_DT_SPEC_GET(node_id)
    • ADC_DT_SPEC_INST_GET(inst)
  • 后面两种方式分别是通过 compatible 属性和节点ID来获取设备树信息的,这里大家可能会有些疑问,为什么通过节点ID就可以获取到 phandle 数组,Zephyr是如何获取到我们的信息是保存在哪个属性中的呢?

  • 这个答案我们可以从上面的展开结果中找到,在展开之后我们看到这个宏丢弃了一个参数,这个参数就是 io_channels,那么为什么不使用呢,这是由于在 ADC_DT_SPEC_GET_BY_IDX 已经包含了 io_channels,因此也就自动忽略了 io_channels 参数,换句话说, 如果你自定义一个其他的节点,要想通过上面的宏获取设备树信息,那么其中必须包含 io-channels 属性。

#define ADC_DT_SPEC_GET_BY_IDX(node_id, idx) \
	ADC_DT_SPEC_STRUCT(DT_IO_CHANNELS_CTLR_BY_IDX(node_id, idx), \
			   DT_IO_CHANNELS_INPUT_BY_IDX(node_id, idx))
  • 下面是Zephyr的示例代码
#define DT_ADC_NODE_SPEC_GET(node_id, prop, index) \
	ADC_DT_SPEC_GET_BY_IDX(DT_N_S_zephyr_user, index),

static const struct adc_dt_spec adc_channels[] = {
	DT_FOREACH_PROP_ELEM(DT_PATH(zephyr_user), io_channels, DT_ADC_NODE_SPEC_GET)
};

void main(void)
{
	uint16_t buf = 0;
	struct adc_sequence sequence = {
		.buffer = &buf,
		.buffer_size = sizeof(buf),
	};

	for(size_t i = 0; i < ARRAY_SIZE(adc_channels); i++)
	{
		if (!device_is_ready(adc_channels[i].dev))
		{
			printk("%s channel %d not ready\r\n", adc_channels[i].dev->name, adc_channels[i].channel_id);
		}

		if ( 0 > adc_channel_setup_dt(&adc_channels[i]))
		{
			
			printk("%s channel %d not setup\r\n", adc_channels[i].dev->name, adc_channels[i].channel_id);
		}
	}

	while (1) 
	{
		for(size_t i = 0; i < ARRAY_SIZE(adc_channels); i++)
		{
			adc_sequence_init_dt(&adc_channels[i], &sequence);
			adc_read(adc_channels[i].dev, &sequence);
			printk("%s channel %d : %d\r\n", adc_channels[i].dev->name, adc_channels[i].channel_id, buf);
		}
		k_sleep(K_SECONDS(1));
	}
}
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

咕咚.萌西

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值