Linux驱动开发(速记版)--单总线

news2024/11/23 6:38:32

第124章 单总线简介

124.1 单总线概述

        单总线是一种串行通信协议,由Dallas Semiconductor开发,特点是用一根信号线实现双向数据传输和时钟同步,节省IO口且结构简单。

        它广泛应用于传感器、存储器等。

        硬件包括信号线、上拉电阻、设备和处理器。

        设备通过唯一64位地址识别,处理器控制通信。

124.2 单总线的通信步骤

        单总线是主从结构,当主机呼叫从机时,从机才会应答,所以主机都必须严格遵循单总线的命令时序。如果命令时序不对,则器件不会响应。

单总线的通信步骤通常包括以下几个阶段。

1 初始化:通信开始之前,主设备会发送初始化信号来确保单总线上没有其他设备正在通信。

        初始化信号是一个特定的序列,通常是将数据线拉低一段时间然后释放。

2 ROM 操作命令

3 功能命令

第125章 DS18B20 驱动框架编写

编写 DS18b20 驱动涉及以下几个关键知识点

1 字符设备驱动

2 平台总线

3 设备树

4 解析设备树

5 gpio 子系统

6 pinctrl 子系统

7 单总线协议

125.1 驱动程序的编写

        该代码是一个Linux内核模块,用于实现DS18B20温度传感器的平台驱动。

        它包含了驱动的初始化设备探测(probe)功能以及模块的加载和卸载功能。

// 设备数据结构  
struct ds18b20_data {  
    dev_t dev_num; // 设备号  
    struct cdev ds18b20_cdev; // 字符设备结构  
    struct class *ds18b20_class; // 设备类  
    struct device *ds18b20_device; // 设备  
};  
  
static struct ds18b20_data *ds18b20; // 全局设备数据指针  
  
// 设备文件操作函数  
int ds18b20_open(struct inode *inode, struct file *file)  
{  
    return 0;  
}  
  
ssize_t ds18b20_read(struct file *file, char __user *buf, size_t size, loff_t *offs)  
{  
    // 此处应添加读取设备的实际逻辑  
    return 0; // 表示成功读取了0字节(应修改为实际读取的字节数)  
}  
  
int ds18b20_release(struct inode *inode, struct file *file)  
{  
    return 0;  
}  
  
// 定义设备文件操作结构  
static struct file_operations ds18b20_fops = {  
    .open = ds18b20_open,  
    .read = ds18b20_read,  
    .release = ds18b20_release,  
    .owner = THIS_MODULE,  
};  
  
// 设备探测函数  
int ds18b20_probe(struct platform_device *dev)  
{  
    int ret;  
    printk(KERN_INFO "This is probe\n");  
  
    // 分配设备数据结构内存  
    ds18b20 = kzalloc(sizeof(*ds18b20), GFP_KERNEL);  
    if (!ds18b20) {  
        printk(KERN_ERR "kzalloc error\n");  
        ret = -ENOMEM;  
        goto error_0;  
    }  
  
    // 分配字符设备号  
    ret = alloc_chrdev_region(&ds18b20->dev_num, 0, 1, "myds18b20");  
    if (ret < 0) {  
        printk(KERN_ERR "alloc_chrdev_region error\n");  
        goto error_1;  
    }  
  
    // 初始化字符设备并添加到系统  
    cdev_init(&ds18b20->ds18b20_cdev, &ds18b20_fops);  
    ds18b20->ds18b20_cdev.owner = THIS_MODULE;  
    ret = cdev_add(&ds18b20->ds18b20_cdev, ds18b20->dev_num, 1);  
    if (ret < 0) {  
        printk(KERN_ERR "cdev_add error\n");  
        goto error_2;  
    }  
  
    // 创建设备类  
    ds18b20->ds18b20_class = class_create(THIS_MODULE, "sensors");  
    if (IS_ERR(ds18b20->ds18b20_class)) {  
        printk(KERN_ERR "class_create error\n");  
        ret = PTR_ERR(ds18b20->ds18b20_class);  
        goto error_3;  
    }  
  
    // 创建设备  
    ds18b20->ds18b20_device = device_create(ds18b20->ds18b20_class, NULL, ds18b20->dev_num, NULL, "ds18b20");  
    if (IS_ERR(ds18b20->ds18b20_device)) {  
        printk(KERN_ERR "device_create error\n");  
        ret = PTR_ERR(ds18b20->ds18b20_device);  
        goto error_4;  
    }  
  
    return 0;  
  
error_4:  
    class_destroy(ds18b20->ds18b20_class);  
error_3:  
    cdev_del(&ds18b20->ds18b20_cdev);  
error_2:  
    unregister_chrdev_region(ds18b20->dev_num, 1);  
error_1:  
    kfree(ds18b20);  
error_0:  
    return ret;  
}  
  
// 定义设备匹配表  
static const struct of_device_id ds18b20_match_table[] = {  
    { .compatible = "ds18b20" },  
    { },  
};  
MODULE_DEVICE_TABLE(of, ds18b20_match_table);  
  
// 定义平台驱动  
static struct platform_driver ds18b20_driver = {  
    .driver = {  
        .owner = THIS_MODULE,  
        .name = "ds18b20",  
        .of_match_table = ds18b20_match_table,  
    },  
    .probe = ds18b20_probe,  
};  
  
// 模块初始化函数  
static int __init ds18b20_init(void)  
{  
    int ret;  
    ret = platform_driver_register(&ds18b20_driver);  
    if (ret < 0) {  
        printk(KERN_ERR "platform_driver_register error\n");  
        return -1;  
    }  
    return 0;  
}  
  
// 模块退出函数  
static void __exit ds18b20_exit(void)  
{  
    // 销毁设备  
    device_destroy(ds18b20->ds18b20_class, ds18b20->dev_num);  
    // 销毁设备类  
    class_destroy(ds18b20->ds18b20_class);  
    // 删除字符设备  
    cdev_del(&ds18b20->ds18b20_cdev);  
    // 注销字符设备号  
    unregister_chrdev_region(ds18b20->dev_num, 1);  
    // 释放设备数据结构内存  
    kfree(ds18b20);  
    // 注销平台驱动  
    platform_driver_unregister(&ds18b20_driver);  
}  
  
module_init(ds18b20_init);  
module_exit(ds18b20_exit);  
  
MODULE_LICENSE("GPL");  
MODULE_AUTHOR("Your Name");  
MODULE_DESCRIPTION("DS18B20 Temperature Sensor Driver");

第126章 DS18B20 驱动设备树

126.1 DS18B20 驱动设备树配置

        打开 Linux/linux_sdk/kernel/arch/arm64/boot/dts/rockchip/rk3568-evb1-ddr4-v10.dtsi 文件,在根目录下添加 ds18b20 节点,如下所示:

ds18b20_gpio: gpio0_b0 {    
    // 表示该设备节点兼容ds18b20温度传感器  
    compatible = "ds18b20";    
      
    // 指定ds18b20连接到的GPIO引脚信息  
    // <&gpio0> 表示使用gpio0控制器  
    // RK_PB0 表示引脚号为RK_PB0  
    // GPIO_ACTIVE_HIGH 表示信号高电平有效  
    ds18b20-gpios = <&gpio0 RK_PB0 GPIO_ACTIVE_HIGH>;    
      
    // 定义引脚控制名称,这里使用默认配置  
    pinctrl-names = "default";    
      
    // 指向具体的引脚控制配置节点  
    // <&ds18b20_gpio_ctrl> 引用名为ds18b20_gpio_ctrl的控制配置  
    pinctrl-0 = <&ds18b20_gpio_ctrl>;    
};

        &pinctrl 节点添加如下节点,

ds18b20_gpio {    
    // 定义ds18b20的GPIO控制节点  
    ds18b20_gpio_ctrl: ds18b20-gpio-ctrl {    
        // 配置GPIO引脚信息  
        // 1: 引脚编号(可能表示在GPIO控制器中的索引)  
        // RK_PA0: 引脚地址(具体硬件上的引脚)  
        // RK_FUNC_GPIO: 引脚功能(设置为GPIO功能)  
        // &pcfg_pull_none: 引脚不上拉也不下拉  
        rockchip,pins = <1 RK_PA0 RK_FUNC_GPIO &pcfg_pull_none>;    
    };    
};

        修改完成后,重新编译并烧写内核镜像。

        内核镜像烧写完毕,输入以下命令

cd /proc/device-tree/gpio0_b0/
ls
cat compatible
设备树中DS18B20的GPIO的设备节点的兼容性字符串

         然后 Insmod装载 ds18b20的驱动,

加载的设备驱动列表

 

         至此,DS18b20 设备树配置和字符设备驱动框架已经完成了。

第127章 DS18B20 驱动复位时序编写

127.1 DS18B20 驱动获取 GPIO 编写

        该代码实现了一个完整的Linux内核模块,用于支持DS18B20温度传感器。

        它包含了必要的头文件、数据结构、文件操作、设备探测、初始化、退出函数等。

        在探测函数中,代码进行了内存分配、字符设备注册、设备类和设备的创建、GPIO配置等操作。

        在退出函数中,代码则进行了相应的清理和注销操作。

#include <linux/module.h>  
#include <linux/kernel.h>  
#include <linux/platform_device.h>  
#include <linux/of.h>  
#include <linux/cdev.h>  
#include <linux/device.h>  
#include <linux/gpio/consumer.h>  
#include <linux/fs.h>  
#include <linux/uaccess.h>  
  
// DS18B20 数据结构  
struct ds18b20_data {  
    dev_t dev_num;                // 设备号  
    struct cdev ds18b20_cdev;     // 字符设备结构体  
    struct class *ds18b20_class;  // 设备类  
    struct device *ds18b20_device;// 设备  
    struct gpio_desc *ds18b20_gpio;// GPIO 描述符  
};  
  
static struct ds18b20_data *ds18b20; // DS18B20 数据结构指针  
  
// 文件操作结构体函数  
static int ds18b20_open(struct inode *inode, struct file *file)  
{  
    return 0;  
}  
  
static ssize_t ds18b20_read(struct file *file, char __user *buf, size_t size, loff_t *offs)  
{  
    return 0;  
}  
  
static int ds18b20_release(struct inode *inode, struct file *file)  
{  
    return 0;  
}  
  
static struct file_operations ds18b20_fops = {  
    .open = ds18b20_open,  
    .read = ds18b20_read,  
    .release = ds18b20_release,  
    .owner = THIS_MODULE,  
};  
  
// 设备探测函数  
static int ds18b20_probe(struct platform_device *dev)  
{  
    int ret;  
    printk("This is probe\n");  
  
    // 分配内存给 ds18b20_data 结构体  
    ds18b20 = kzalloc(sizeof(*ds18b20), GFP_KERNEL);  
    if (!ds18b20) {  
        printk("kzalloc error\n");  
        ret = -ENOMEM;  
        goto error_0;  
    }  
  
    // 分配字符设备号  
    ret = alloc_chrdev_region(&ds18b20->dev_num, 0, 1, "myds18b20");  
    if (ret < 0) {  
        printk("alloc_chrdev_region error\n");  
        ret = -EAGAIN;  
        goto error_1;  
    }  
  
    // 初始化字符设备  
    cdev_init(&ds18b20->ds18b20_cdev, &ds18b20_fops);  
    ds18b20->ds18b20_cdev.owner = THIS_MODULE;  
    cdev_add(&ds18b20->ds18b20_cdev, ds18b20->dev_num, 1);  
  
    // 创建设备类  
    ds18b20->ds18b20_class = class_create(THIS_MODULE, "sensors");  
    if (IS_ERR(ds18b20->ds18b20_class)) {  
        printk("class_create error\n");  
        ret = PTR_ERR(ds18b20->ds18b20_class);  
        goto error_2;  
    }  
  
    // 创建设备  
    ds18b20->ds18b20_device = device_create(ds18b20->ds18b20_class, NULL, ds18b20->dev_num, NULL, "ds18b20");  
    if (IS_ERR(ds18b20->ds18b20_device)) {  
        printk("device_create error\n");  
        ret = PTR_ERR(ds18b20->ds18b20_device);  
        goto error_3;  
    }  
  
    // 获取 GPIO 描述符  
    ds18b20->ds18b20_gpio = gpiod_get_optional(&dev->dev, "ds18b20", 0);  
    if (!ds18b20->ds18b20_gpio) {  
        ret = -EBUSY;  
        goto error_4;  
    }  
  
    // 设置 GPIO 方向为输出  
    gpiod_direction_output(ds18b20->ds18b20_gpio, 1);  
  
    return 0;  
  
error_4:  
    device_destroy(ds18b20->ds18b20_class, ds18b20->dev_num);  
error_3:  
    class_destroy(ds18b20->ds18b20_class);  
error_2:  
    cdev_del(&ds18b20->ds18b20_cdev);  
    unregister_chrdev_region(ds18b20->dev_num, 1);  
error_1:  
    kfree(ds18b20);  
error_0:  
    return ret;  
}  
  
// 设备匹配表  
static const struct of_device_id ds18b20_match_table[] = {  
    { .compatible = "ds18b20" },  
    {},  
};  
  
// 平台驱动结构体  
static struct platform_driver ds18b20_driver = {  
    .driver = {  
        .owner = THIS_MODULE,  
        .name = "ds18b20",  
        .of_match_table = ds18b20_match_table,  
    },  
    .probe = ds18b20_probe,  
};  
  
// 模块初始化函数  
static int __init ds18b20_init(void)  
{  
    int ret;  
    ret = platform_driver_register(&ds18b20_driver);  
    if (ret < 0) {  
        printk("platform_driver_register error\n");  
        return -1;  
    }  
    return 0;  
}  
  
// 模块退出函数  
static void __exit ds18b20_exit(void)  
{  
    // 释放 GPIO 描述符  
    gpiod_put(ds18b20->ds18b20_gpio);  
  
    // 销毁设备  
    device_destroy(ds18b20->ds18b20_class, ds18b20->dev_num);  
  
    // 销毁设备类  
    class_destroy(ds18b20->ds18b20_class);  
  
    // 删除字符设备  
    cdev_del(&ds18b20->ds18b20_cdev);  
  
    // 注销字符设备号  
    unregister_chrdev_region(ds18b20->dev_num, 1);  
  
    // 释放 ds18b20_data 内存  
    kfree(ds18b20);  
  
    // 注销平台驱动  
    platform_driver_unregister(&ds18b20_driver);  
}  
  
module_init(ds18b20_init);  
module_exit(ds18b20_exit);  
MODULE_LICENSE("GPL");

127.2 DS18b20 驱动复位时序编写

#include <linux/module.h>  
#include <linux/kernel.h>  
#include <linux/platform_device.h>  
#include <linux/of.h>  
#include <linux/cdev.h>  
#include <linux/device.h>  
#include <linux/gpio/consumer.h>  
#include <linux/fs.h>  
#include <linux/uaccess.h>  
  
struct ds18b20_data {  
    dev_t dev_num;                  // 设备号  
    struct cdev ds18b20_cdev;       // 字符设备结构体  
    struct class *ds18b20_class;    // 设备类  
    struct device *ds18b20_device;  // 设备  
    struct gpio_desc *ds18b20_gpio; // GPIO 描述符  
};  
  
static struct ds18b20_data *ds18b20; // DS18B20 数据结构指针  
  
static int ds18b20_open(struct inode *inode, struct file *file)  
{  
    return 0;  
}  
  
static ssize_t ds18b20_read(struct file *file, char __user *buf, size_t size, loff_t *offs)  
{  
    return 0;  
}  
  
static int ds18b20_release(struct inode *inode, struct file *file)  
{  
    return 0;  
}  
  
static struct file_operations ds18b20_fops = {  
    .open = ds18b20_open,  
    .read = ds18b20_read,  
    .release = ds18b20_release,  
    .owner = THIS_MODULE,  
};  
  
// 设备探测函数  
static int ds18b20_probe(struct platform_device *dev)  
{  
    int ret;  
    printk("This is probe\n");  
  
    // 分配内存给 ds18b20_data 结构体  
    ds18b20 = kzalloc(sizeof(*ds18b20), GFP_KERNEL);  
    if (!ds18b20) {  
        printk("kzalloc error\n");  
        ret = -ENOMEM;  
        goto error_0;  
    }  
  
    // 分配字符设备号  
    ret = alloc_chrdev_region(&ds18b20->dev_num, 0, 1, "myds18b20");  
    if (ret < 0) {  
        printk("alloc_chrdev_region error\n");  
        ret = -EAGAIN;  
        goto error_1;  
    }  
  
    // 初始化字符设备  
    cdev_init(&ds18b20->ds18b20_cdev, &ds18b20_fops);  
    ds18b20->ds18b20_cdev.owner = THIS_MODULE;  
    cdev_add(&ds18b20->ds18b20_cdev, ds18b20->dev_num, 1);  
  
    // 创建设备类  
    ds18b20->ds18b20_class = class_create(THIS_MODULE, "sensors");  
    if (IS_ERR(ds18b20->ds18b20_class)) {  
        printk("class_create error\n");  
        ret = PTR_ERR(ds18b20->ds18b20_class);  
        goto error_2;  
    }  
  
    // 创建设备  
    ds18b20->ds18b20_device = device_create(ds18b20->ds18b20_class, NULL, ds18b20->dev_num, NULL, "ds18b20");  
    if (IS_ERR(ds18b20->ds18b20_device)) {  
        printk("device_create error\n");  
        ret = PTR_ERR(ds18b20->ds18b20_device);  
        goto error_3;  
    }  
  
    // 获取 GPIO 描述符  
    ds18b20->ds18b20_gpio = gpiod_get_optional(&dev->dev, "ds18b20", 0);  
    if (!ds18b20->ds18b20_gpio) {  
        ret = -EBUSY;  
        goto error_4;  
    }  
  
    // 设置 GPIO 方向为输出  
    gpiod_direction_output(ds18b20->ds18b20_gpio, 1);  
  
    return 0;  
  
error_4:  
    device_destroy(ds18b20->ds18b20_class, ds18b20->dev_num);  
error_3:  
    class_destroy(ds18b20->ds18b20_class);  
error_2:  
    cdev_del(&ds18b20->ds18b20_cdev);  
    unregister_chrdev_region(ds18b20->dev_num, 1);  
error_1:  
    kfree(ds18b20);  
error_0:  
    return ret;  
}  
  
// 设备匹配表  
static const struct of_device_id ds18b20_match_table[] = {  
    { .compatible = "ds18b20" },  
    {},  
};  
  
static struct platform_driver ds18b20_driver = {  
    .driver = {  
        .owner = THIS_MODULE,  
        .name = "ds18b20",  
        .of_match_table = ds18b20_match_table,  
    },  
    .probe = ds18b20_probe,  
};  
  
static int __init ds18b20_init(void)  
{  
    int ret;  
    ret = platform_driver_register(&ds18b20_driver);  
    if (ret < 0) {  
        printk("platform_driver_register error\n");  
        return -1;  
    }  
    return 0;  
}  
  
static void __exit ds18b20_exit(void)  
{  
    // 释放 GPIO 描述符  
    gpiod_put(ds18b20->ds18b20_gpio);  
  
    // 销毁设备  
    device_destroy(ds18b20->ds18b20_class, ds18b20->dev_num);  
  
    // 销毁设备类  
    class_destroy(ds18b20->ds18b20_class);  
  
    cdev_del(&ds18b20->ds18b20_cdev);  
  
    // 注销字符设备号  
    unregister_chrdev_region(ds18b20->dev_num, 1);  
  
    // 释放 ds18b20_data 内存  
    kfree(ds18b20);  
  
    // 注销平台驱动  
    platform_driver_unregister(&ds18b20_driver);  
}  
  
module_init(ds18b20_init);  
module_exit(ds18b20_exit);  
MODULE_LICENSE("GPL");

        硬件连接好之后,安装逻辑分析仪的上位机软件,安装完成之后,打开上位机软件,然后设置参数,

 

设置完毕之后,如下图所示:

上位机上捕捉到时序如下图所示: 

将捕捉到的复位时序和下图的时序图对比分析,可以看出驱动复位时序是没问题的。

        至此,DS18b20 驱动复位时序编写完成。

第128章 DS18B20 驱动写时序编写

         驱动入口函数调用了 ds18b20的写字节函数,

        写字节函数调用写位函数,

        写位函数调用 gpiod接口,

        gpiod接口基于 gpio描述符。

#include <linux/module.h>  
#include <linux/kernel.h>  
#include <linux/platform_device.h>  
#include <linux/of.h>  
#include <linux/gpio/consumer.h>  
#include <linux/uaccess.h>  
#include <linux/cdev.h>  
#include <linux/fs.h>  
#include <linux/delay.h> // for udelay  
  
struct ds18b20_data {  
    dev_t dev_num;  
    struct cdev ds18b20_cdev;  
    struct class *ds18b20_class;  
    struct device *ds18b20_device;  
    struct gpio_desc *ds18b20_gpio;  
};  
  
static struct ds18b20_data *ds18b20;  
  
// 复位 DS18B20  
static void ds18b20_reset(void) {  
    gpiod_direction_output(ds18b20->ds18b20_gpio, 1);  
    gpiod_set_value(ds18b20->ds18b20_gpio, 0);  
    udelay(700);  
    gpiod_set_value(ds18b20->ds18b20_gpio, 1);  
    gpiod_direction_input(ds18b20->ds18b20_gpio);  
    while (gpiod_get_value(ds18b20->ds18b20_gpio));  
    while (!gpiod_get_value(ds18b20->ds18b20_gpio));  
    udelay(480);  
}  
  
// 向 DS18B20 写入单个位  
static void ds18b20_writebit(unsigned char bit) {  
    gpiod_direction_output(ds18b20->ds18b20_gpio, 1);  
    gpiod_set_value(ds18b20->ds18b20_gpio, 0);  
    if (bit) {  
        udelay(10);  
        gpiod_direction_output(ds18b20->ds18b20_gpio, 1);  
    }  
    udelay(65);  
    gpiod_direction_output(ds18b20->ds18b20_gpio, 1);  
    udelay(2);  
}  
  
// 向 DS18B20 写入一个字节  
static void ds18b20_writebyte(int data) {  
    int i;  
    for (i = 0; i < 8; i++) {  
        ds18b20_writebit(data & 0x01);  
        data >>= 1;  
    }  
}  
  
// 字符设备操作函数  
static int ds18b20_open(struct inode *inode, struct file *file) {  
    return 0;  
}  
  
static ssize_t ds18b20_read(struct file *file, char __user *buf, size_t size, loff_t *offs) {  
    return 0;  
}  
  
static int ds18b20_release(struct inode *inode, struct file *file) {  
    return 0;  
}  
  
static struct file_operations ds18b20_fops = {  
    .open = ds18b20_open,  
    .read = ds18b20_read,  
    .release = ds18b20_release,  
    .owner = THIS_MODULE,  
};  
  
// 设备探测函数  
static int ds18b20_probe(struct platform_device *dev) {  
    int ret;  
    printk("This is probe\n");  
  
    ds18b20 = kzalloc(sizeof(*ds18b20), GFP_KERNEL);  
    if (!ds18b20) {  
        printk("kzalloc error\n");  
        ret = -ENOMEM;  
        goto error_0;  
    }  
  
    ret = alloc_chrdev_region(&ds18b20->dev_num, 0, 1, "myds18b20");  
    if (ret < 0) {  
        printk("alloc_chrdev_region error\n");  
        ret = -EAGAIN;  
        goto error_1;  
    }  
  
    cdev_init(&ds18b20->ds18b20_cdev, &ds18b20_fops);  
    ds18b20->ds18b20_cdev.owner = THIS_MODULE;  
    cdev_add(&ds18b20->ds18b20_cdev, ds18b20->dev_num, 1);  
  
    ds18b20->ds18b20_class = class_create(THIS_MODULE, "sensors");  
    if (IS_ERR(ds18b20->ds18b20_class)) {  
        printk("class_create error\n");  
        ret = PTR_ERR(ds18b20->ds18b20_class);  
        goto error_2;  
    }  
  
    ds18b20->ds18b20_device = device_create(ds18b20->ds18b20_class, NULL, ds18b20->dev_num, NULL, "ds18b20");  
    if (IS_ERR(ds18b20->ds18b20_device)) {  
        printk("device_create error\n");  
        ret = PTR_ERR(ds18b20->ds18b20_device);  
        goto error_3;  
    }  
  
    ds18b20->ds18b20_gpio = gpiod_get_optional(&dev->dev, "ds18b20", 0);  
    if (!ds18b20->ds18b20_gpio) {  
        ret = -EBUSY;  
        goto error_4;  
    }  
  
    gpiod_direction_output(ds18b20->ds18b20_gpio, 1);  
    return 0;  
  
error_4:  
    device_destroy(ds18b20->ds18b20_class, ds18b20->dev_num);  
error_3:  
    class_destroy(ds18b20->ds18b20_class);  
error_2:  
    cdev_del(&ds18b20->ds18b20_cdev);  
    unregister_chrdev_region(ds18b20->dev_num, 1);  
error_1:  
    kfree(ds18b20);  
error_0:  
    return ret;  
}  
  
static const struct of_device_id ds18b20_match_table[] = {  
    { .compatible = "ds18b20" },  
    { },  
};  
  
static struct platform_driver ds18b20_driver = {  
    .driver = {  
        .owner = THIS_MODULE,  
        .name = "ds18b20",  
        .of_match_table = ds18b20_match_table,  
    },  
    .probe = ds18b20_probe,  
};  
  
static int __init ds18b20_init(void) {  
    int ret;  
    ret = platform_driver_register(&ds18b20_driver);  
    if (ret < 0) {  
        printk("platform_driver_register error\n");  
        return -1;  
    }  
    ds18b20_reset();  
    ds18b20_writebyte(0xcc); // 写入 0Xcc 字符(跳过 ROM 命令)  
    return 0;  
}  
  
static void __exit ds18b20_exit(void) {  
    gpiod_put(ds18b20->ds18b20_gpio);  
    device_destroy(ds18b20->ds18b20_class, ds18b20->dev_num);  
    class_destroy(ds1

第129章 DS18B20 驱动读时序编写

        读时序参考STM32和写时序的套路。

第130章 DS18B20读温度

        字符设备的 file_operations的读操作给了 ds18b20的读取代码。

  
// 定义DS18B20数据结构体  
struct ds18b20_data {  
    // ... 其他成员变量(省略)  
    struct gpio_desc *ds18b20_gpio; // GPIO描述符指针  
};  
  
// 声明DS18B20数据结构体指针  
struct ds18b20_data *ds18b20;  
  
// 复位DS18B20传感器  
void ds18b20_reset(void) {  
    // ... 复位操作(省略)  
}  
  
// 向DS18B20写入单个位  
void ds18b20_writebit(unsigned char bit) {  
    // ... 写位操作(省略)  
}  
  
// 向DS18B20写入一个字节  
void ds18b20_writebyte(int data) {  
    // ... 写字节操作(使用ds18b20_writebit,省略)  
}  
  
// 从DS18B20读取单个位  
unsigned char ds18b20_readbit(void) {  
    // ... 读位操作(省略)  
    return bit;  
}  
  
// 从DS18B20读取一个字节  
int ds18b20_readbyte(void) {  
    // ... 读字节操作(使用ds18b20_readbit,省略)  
    return data;  
}  
  
// 从DS18B20读取温度值  
int ds18b20_readtemp(void) {  
    int temp_l, temp_h, temp;  
    ds18b20_reset(); // 复位传感器  
    ds18b20_writebyte(0xCC); // 发送跳过ROM命令  
    ds18b20_writebyte(0x44); // 发送启动温度转换命令  
    mdelay(750); // 延时等待温度转换完成  
    ds18b20_reset(); // 再次复位传感器  
    ds18b20_writebyte(0xCC); // 发送跳过ROM命令  
    ds18b20_writebyte(0xBE); // 发送读取温度值命令  
    temp_l = ds18b20_readbyte(); // 读取温度低位字节  
    temp_h = ds18b20_readbyte(); // 读取温度高位字节  
    temp = (temp_h << 8) | temp_l; // 组合成完整的温度值  
    return temp;  
}  
  
// 字符设备读取函数  
ssize_t ds18b20_read(struct file *file, char __user *buf, size_t size, loff_t *offs) {  
    int ds18b20_temp;  
    ds18b20_temp = ds18b20_readtemp(); // 读取温度值  
    if (copy_to_user(buf, &ds18b20_temp, sizeof(ds18b20_temp))) {  
        return -1; // 复制失败,返回错误  
    }  
    return 0; // 成功读取并复制温度值  
}  

struct file_operations ds18b20_fops = {
    .open = ds18b20_open, 
    .read = ds18b20_read, 
    .release = ds18b20_release, 
    .unlocked_ioctl = ds18b20_ioctl, 
    .owner = THIS_MODULE, };

// 初始化字符设备
cdev_init(&ds18b20->ds18b20_cdev, &ds18b20_fops);
ds18b20->ds18b20_cdev.owner = THIS_MODULE;
cdev_add(&ds18b20->ds18b20_cdev, ds18b20->dev_num, 1);

//初始化设备类
...
//初始化设备
...

// ... 其他代码(如模块初始化、退出等,省略)
/*在应用程序中*/

// 设置分辨率
ioctl(fd, SET_RESOLUTION, args);
while (1) {
// 读取数据
read(fd, &data, sizeof(data));
// 处理并打印温度信息
ds18b20_get_temp(data);

第131章 DS18B20设置分辨率

/*读取分辨率*/
int read_resolution(void) {
    int ret;
    // 复位传感器
    ds18b20_reset();
    // 发送指令字节 0xCC,跳过 ROM 操作,直接与单个设备通信
    ds18b20_writebyte(0xCC);
    // 发送指令字节 0xBE,读取当前设备的配置寄存器
    ds18b20_writebyte(0xBE);
    // 读取 4 个字节的数据,但实际上只有最后一个字节是分辨率信息
    ds18b20_readbyte();
    ds18b20_readbyte();
    ds18b20_readbyte();
    ds18b20_readbyte();
    // 读取最后一个字节,即分辨率信息
    ret = ds18b20_readbyte();
    // 返回分辨率值
    return ret;
}

/**
* DS18B20 温度传感器的 ioctl 函数
* @param file 文件指针
* @param cmd 命令
* @param args 参数
* @return 返回执行结果,成功返回 0,失败返回 -1
*/
long ds18b20_ioctl(struct file *file, unsigned int cmd, unsigned long args) {
    int resolution;
    if (cmd == SET_RESOLUTION) { // 判断命令是否为设置分辨率
        if (args >= SET_RESOLUTION_9 && args <= SET_RESOLUTION_12) { // 判断参数是否在有效的分辨率范围内
            set_resolution(args); // 调用设置分辨率的函数
            return 0; // 返回成功
        }
    }
    else if (cmd == READ_RESOLUTION) {
    // 读取分辨率
    resolution = read_resolution();
    // 将分辨率的值复制给用户空间的 args
    if (copy_to_user((int *)args, &resolution, sizeof(resolution))) {
    // 复制失败,返回-1 表示失败
        return -1;
        }
    }
    // 如果不匹配 SET_RESOLUTION 或者 args 不在有效范围内,不执行任何操作
    return -1; // 返回失败
}

struct file_operations ds18b20_fops = {
    .open = ds18b20_open, 
    .read = ds18b20_read, 
    .release = ds18b20_release, 
    .unlocked_ioctl = ds18b20_ioctl, 
    .owner = THIS_MODULE, };
/*在应用程序中*/

// 设置分辨率
ioctl(fd, SET_RESOLUTION, args);
// 读取分辨率
ioctl(fd, READ_RESOLUTION, &resolution);
ds18b20_get_resolution(resolution);
while (1) {
// 读取数据
read(fd, &data, sizeof(data));
// 处理并打印温度信息
ds18b20_get_temp(data);

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2195993.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

代码随想录--字符串--重复的子字符串

题目 给定一个非空的字符串&#xff0c;判断它是否可以由它的一个子串重复多次构成。给定的字符串只含有小写英文字母&#xff0c;并且长度不超过10000。 示例 1: 输入: "abab" 输出: True 解释: 可由子字符串 "ab" 重复两次构成。示例 2: 输入: "…

javaweb - 请求响应代码实现

简单参数 原始方式 在原始的web程序中&#xff0c;获取请求参数&#xff0c;需要通过HttpServletRequest对象手动获取。 RequestMapping("/simpleParam")public String simpleParam(HttpServletRequest request) {String name request.getParameter("name&…

基于Springboot+Vue的养老院管理系统的设计与实现 (含源码数据库)

1.开发环境 开发系统:Windows10/11 架构模式:MVC/前后端分离 JDK版本: Java JDK1.8 开发工具:IDEA 数据库版本: mysql5.7或8.0 数据库可视化工具: navicat 服务器: SpringBoot自带 apache tomcat 主要技术: Java,Springboot,mybatis,mysql,vue 2.视频演示地址 3.功能 这个系…

TMC2209堵转检测配置详细教程

按如下图将TMC2209和串口模块连接&#xff1a; 配置流程&#xff1a; 寄存器配置&#xff1a; 通用寄存器&#xff1a; 校验码见我之前文章&#xff1a;使用uart串口配置TMC2209模块_tmc2209 uart-CSDN博客 通用寄存器主要是配置第0和第1位&#xff0c;第1位要给1&#xff0…

python之详解列表

有序的可变容器&#xff0c;可以存储不同类型的元素。用中括号[]表示。 1、列表的查找访问 1.1、通过下标查找。 与字符串类似&#xff0c;列表也可通过 列表名[index] 的方式查找其中的元素。 索引的初始值为0&#xff0c;最大值为列表长度-1。 示例&#xff1a; list1 …

刷题 位运算 / 数学

面试经典 150 题 - 位运算 ⭐️⭐️67. 二进制求和 加法进位 class Solution { public:string addBinary(string a, string b) {int na a.size(), nb b.size();string ans;ans.reserve(max(na, nb) 1); // 预留空间&#xff0c;避免动态扩展时的性能损耗int carry 0;for …

platform bus平台总线详解

往期内容 驱动中的device和device_driver结构体-CSDN博客bus总线的相关结构体和注册逻辑-CSDN博客bus中设备驱动的probe触发逻辑和device、driver的添加逻辑-CSDN博客 前言 注&#xff1a;以下的代码皆摘自于linux 4.9.88版本的内核源码&#xff0c;不同版本可能有所出入。 之…

【EXCEL数据处理】保姆级教程 000016案例 EXCEL的vlookup函数。

【EXCEL数据处理】000016案例 vlookup函数。 前言&#xff1a;哈喽&#xff0c;大家好&#xff0c;今天给大家分享一篇文章&#xff01;创作不易&#xff0c;如果能帮助到大家或者给大家一些灵感和启发&#xff0c;欢迎收藏关注哦 &#x1f495; 目录 【EXCEL数据处理】保姆级教…

【动态规划】斐波那契模型 dp

动态规划的步骤&#xff1a; 状态表示。所谓状态表示就是 dp 表里的值表示什么含义&#xff0c;那么状态表示怎么找呢&#xff1f; a. 题目要求 b. 经验&#xff08;以某一个位置为结尾 / 起点&#xff09; 题目要求 c. 分析问题的过程中发现重复子问题状态转移方程。dp[ i ] …

ChatGPT Canvas:系统提示词泄漏了~

OpenAI 推出了一款叫做 Canvas 的新工具&#xff0c;用来帮助用户更好地与 ChatGPT 协作写作和编程。详细介绍可以看这篇文章&#xff1a;ChatGPT Canvas&#xff1a;交互式对话编辑器-CSDN博客​编辑 以下是 OpenAI 新功能 “Canvas” 的系统提示内容 你是 ChatGPT&#xff0…

基于连续小波变换(CWT)批量生成一维信号的时频图 最终生成30张时频图。生成的图像可用于后续的深度学习分类或其他处理。附详细的说明文档。

Matlab基于连续小波变换&#xff08;CWT&#xff09;&#xff0c;将一维信号批量生成时频图的源代码。此示例中&#xff0c;原始信号data是30*1280的格式&#xff0c;一共30条信号&#xff0c;信号长度为1280。最终生成30张时频图。生成的图像可用于后续的深度学习分类或其他处…

SpringBoot开发——SpringSecurity安全框架17个业务场景案例(二)

文章目录 一、Spring Security 常用应用场景介绍二、Spring Security场景案例6、CSRF 保护(CSRF Protection)6.1 Spring Security 配置6.2 业务逻辑代码7、密码编码(Password Encoding)7.1 Spring Security 配置7.2 业务逻辑代码7.3 控制器8、方法级安全性(Method Securit…

Vue2电商项目(八) 完结撒花:图片懒加载、路由懒加载、打包的map文件

一、图片懒加载 安装&#xff1a;npm i vue-lazyload1.3 -s &#xff08;弹幕建议按1.3版本&#xff09; 引入 // 引入懒加载的图片 import hlw from /assets/hulu.jpg // 引入插件 import VueLazyload from vue-lazyload // 引入插件 Vue.use(VueLazyload, {// 懒加载默认的图…

Oracle登录报错-ORA-01017: invalid username/password;logon denied

接上文&#xff1a;Oracle创建用户报错-ORA-65096: invalid common user or role name 我以为 按照上文在PDB里创建了用户&#xff0c;我以为就可以用PLSQL远程连接了&#xff0c;远程服务器上也安装了对应版本的Oracle客户端&#xff0c;但是我想多了&#xff0c;客户只是新建…

【爬虫】网站反debugger、内存爆破以及网站限制开发者工具

【爬虫】网站反debugger、内存爆破以及网站直接限制开发者工具 声明&#xff1a;本文中所有内容仅供学习交流使用&#xff0c;不用于其他任何目的&#xff0c;不提供完整代码&#xff0c;敏感网址、数据接口等均已做脱敏处理&#xff0c;严禁用于商业用途和非法用途&#xff0…

JWT集成Keycloak

一、直接使用现有域账号、密码获取token方式 1.KeyClack 使用现有配置 Client id : account-console 2.服务配置文件配置 3.API接口配置 4. 获取token 5.调用方式&#xff08;Swagger&#xff09;(代码方式直接在请求头加上token) 5.1 配置在Swagger访问 5.2 访问需要认证的接…

JavaWeb——Vue路由(概述、介绍、使用、解决bug)

目录 概述 介绍 使用 解决bug 概述 员工管理的页面已经制作完成。其他页面的制作方式一致。 项目中准备了部门管理的页面组件 DeptView &#xff0c;这样就有了员工管理和部门管理两个页面组件。 在系统中&#xff0c;要实现点击部门管理菜单展示部门管理组件&#xff0c…

为什么Java不支持多重继承?

不支持多重继承主要是因为会产生“菱形继承”&#xff0c;也称为钻石继承的问题。 那什么是菱形继承呢&#xff1f; 它涉及到一个类继承两个父类&#xff0c;而这两个父类又继承自同一个祖先类。这种结构在没有适当处理的情况下&#xff0c;会导致继承层次中的歧义和冗余。 如…

关于BSV区块链覆盖网络的常见问题解答(下篇)

​​发表时间&#xff1a;2024年9月20日 在BSV区块链上的覆盖网络服务为寻求可扩展、安全、高效交易处理解决方案的开发者和企业家开辟了新的视野。 作为开创性的曼达拉升级的一部分&#xff0c;覆盖网络服务提供了一个强大的框架&#xff0c;用于管理特定类型的交易和数据访问…

【目标检测】室内地板砖铺设缺陷检测数据集2000张VOC+YOLO格式

数据集格式&#xff1a;Pascal VOC格式YOLO格式(不包含分割路径的txt文件&#xff0c;仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数)&#xff1a;2002 标注数量(xml文件个数)&#xff1a;2002 标注数量(txt文件个数)&#xff1a;2002 标注…