从单片机到Linux内核:手把手教你将DS18B20驱动移植到野火i.MX6ULL开发板
从单片机到Linux内核:手把手教你将DS18B20驱动移植到野火i.MX6ULL开发板
对于习惯了单片机裸机开发的工程师来说,第一次接触Linux驱动开发往往会感到无所适从。本文将带你完成一次完整的知识迁移——将一个常见的DS18B20温度传感器驱动从单片机环境移植到Linux平台。我们会重点对比两种开发模式的差异,特别是如何将裸机时序操作代码封装进Linux的platform driver框架。
1. 开发环境与硬件准备
在开始之前,我们需要准备好开发环境。野火i.MX6ULL开发板是一款基于Cortex-A7内核的嵌入式Linux开发平台,它提供了完整的Linux BSP支持。与单片机开发不同,Linux驱动开发需要以下工具链:
- 交叉编译工具链 :arm-linux-gnueabihf-
- 内核源码树 :需要与目标板运行的内核版本一致
- 设备树编译器 (dtc):用于编译设备树源文件
硬件连接方面,DS18B20与i.MX6ULL的连接非常简单,只需要一个GPIO引脚:
| 信号线 | 连接说明 |
|---|---|
| DQ | 连接到GPIO1_IO02 |
| VCC | 3.3V电源 |
| GND | 地线 |
提示:DS18B20是开漏输出,需要接上拉电阻(通常4.7KΩ)
2. 设备树配置:硬件描述的革新
设备树是Linux驱动开发与单片机开发最大的区别之一。在单片机中,我们通常在代码中直接写死硬件配置,而在Linux中,这些信息应该放在设备树中。
对于DS18B20,我们需要在设备树中添加以下节点:
/dts-v1/;
/ {
ds18b20 {
compatible = "xgj,ds18b20";
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_ds18b20>;
ds18b20-gpios = <&gpio1 2 GPIO_ACTIVE_HIGH>;
status = "okay";
};
};
&iomuxc {
pinctrl_ds18b20: ds18b20grp {
fsl,pins = <
MX6UL_PAD_GPIO1_IO02__GPIO1_IO02 0x10b0
>;
};
};
设备树编译命令:
make ARCH=arm dtbs
设备树的优势在于:
- 硬件与驱动分离 :同一驱动可以支持不同硬件配置
- 运行时配置 :无需重新编译内核即可修改硬件参数
- 自我描述 :系统可以动态发现硬件配置
3. 驱动框架:从裸机到Linux的思维转变
在单片机环境下,我们通常直接操作寄存器或库函数来控制硬件。而在Linux中,驱动需要遵循内核提供的框架。对于DS18B20这样的设备,最适合使用platform驱动框架。
3.1 platform驱动框架组成
一个完整的platform驱动包含以下部分:
- platform_driver结构体 :定义probe/remove等回调函数
- of_device_id表 :用于设备树匹配
- file_operations :定义设备文件操作接口
- 字符设备注册 :创建/dev节点
关键数据结构示例:
static struct platform_driver ds18b20_driver = {
.probe = ds18b20_probe,
.remove = ds18b20_remove,
.driver = {
.name = "ds18b20",
.of_match_table = ds18b20_of_match,
},
};
3.2 驱动初始化流程对比
| 步骤 | 单片机实现 | Linux驱动实现 |
|---|---|---|
| GPIO初始化 | 直接配置寄存器 | 通过设备树获取GPIO编号 |
| 硬件访问 | 直接操作寄存器 | 使用gpiolib接口 |
| 时序控制 | 直接延时循环 | 使用内核延时函数 |
| 并发保护 | 通常不考虑 | 需要自旋锁/互斥锁 |
4. 核心时序操作的实现与优化
DS18B20的通信依赖于精确的时序,这在Linux环境中面临新的挑战:
4.1 复位时序的实现
static int ds18b20_reset(void)
{
int ret = 1;
unsigned long flags;
spin_lock_irqsave(&ds18b20_dev.lock, flags);
gpio_direction_output(ds18b20_dev.gpio, 0);
udelay(480);
gpio_direction_output(ds18b20_dev.gpio, 1);
udelay(75);
gpio_direction_input(ds18b20_dev.gpio);
ret = gpio_get_value(ds18b20_dev.gpio);
udelay(10);
gpio_direction_output(ds18b20_dev.gpio, 1);
spin_unlock_irqrestore(&ds18b20_dev.lock, flags);
return ret;
}
注意:Linux不是实时系统,udelay的精度有限,对于特别严格的时序可能需要其他方案
4.2 位读写操作的优化
读一位的典型实现:
static uint8_t ds18b20_read_bit(void)
{
uint8_t bit;
gpio_direction_output(ds18b20_dev.gpio, 0);
udelay(1);
gpio_direction_input(ds18b20_dev.gpio);
udelay(5);
bit = gpio_get_value(ds18b20_dev.gpio);
udelay(55);
return bit;
}
为了提高性能,可以考虑以下优化:
- 使用内核hrtimer实现更精确的定时
- 预编译时序模板减少运行时计算
- 实现DMA-based位操作(对于某些SoC)
5. 温度数据的读取与用户接口
在Linux驱动中,我们需要通过文件接口向用户空间暴露温度数据:
5.1 文件操作接口实现
static ssize_t ds18b20_read(struct file *filp, char __user *buf,
size_t size, loff_t *offset)
{
uint8_t temp_L, temp_H;
short temp;
int ret;
if (ds18b20_reset() != 0)
return -EIO;
ds18b20_write_byte(CMD_SKIP_ROM);
ds18b20_write_byte(CMD_CONVERT_TEMP);
msleep(750);
if (ds18b20_reset() != 0)
return -EIO;
ds18b20_write_byte(CMD_SKIP_ROM);
ds18b20_write_byte(CMD_READ_SCRATCHPAD);
temp_L = ds18b20_read_byte();
temp_H = ds18b20_read_byte();
temp = (temp_H << 8) | temp_L;
if (copy_to_user(buf, &temp, sizeof(temp)))
return -EFAULT;
return sizeof(temp);
}
5.2 用户空间测试程序
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#define DEV_PATH "/dev/ds18b20"
int main()
{
int fd = open(DEV_PATH, O_RDONLY);
short raw_temp;
float temperature;
while(1) {
if(read(fd, &raw_temp, sizeof(raw_temp)) == sizeof(raw_temp)) {
temperature = raw_temp * 0.0625f;
printf("Temperature: %.2f°C\n", temperature);
}
sleep(1);
}
close(fd);
return 0;
}
6. 调试技巧与性能考量
Linux驱动调试比单片机复杂得多,以下是一些实用技巧:
调试方法对比表 :
| 调试需求 | 单片机方案 | Linux方案 |
|---|---|---|
| 打印调试 | 串口printf | printk + dmesg |
| 信号测量 | 逻辑分析仪 | ftrace/systrace |
| 内存检查 | 单步调试 | kdump/coredump |
| 性能分析 | 周期计数 | perf工具 |
对于时序敏感型设备,还需要特别注意:
-
禁止内核抢占
:
preempt_disable() -
关闭本地中断
:
local_irq_save() -
内存屏障
:
rmb()/wmb() -
缓存一致性
:
dma_cache_sync()
驱动加载与测试流程:
# 编译驱动
make -C /path/to/kernel M=$PWD modules
# 加载驱动
insmod ds18b20.ko
# 查看内核日志
dmesg | tail
# 运行测试程序
./ds18b20_test
openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构
更多推荐

所有评论(0)