当前位置: 首页 > news >正文

platform平台驱动、gpio单总线通信

前瞻

一、

不需要总线的:led灯控制、beep、按键

需要总线通信的:

        基于gpio的单总线通信的传感器:ds18b20、dht11、超声波测距

        基于i2c总线:LM75

        基于spi总线:ADXL345

总线通信需要总线控制器

总线协议在软件代码的体现其实就是按照传感器时序图进行复位、读写操作。

所以单总线协议就是指按照传感器手册的复位时序图进行复位,然后用读写时序图读写,就算是单总线协议了吗

是的,单总线协议(1-Wire)的核心就是严格按照传感器手册的时序要求进行复位、读/写操作。你的理解基本正确,但需要补充一些关键细节:


1. 单总线协议的本质

单总线协议是一种 基于严格时序的串行通信协议,其核心包括:

  1. 复位脉冲(Reset):主机拉低总线 480µs~960µs,然后释放,等待从机(如 DS18B20)回应一个低电平(60µs~240µs),表示设备存在。

  2. 读写时序

    • 写 0:主机拉低总线 60µs~120µs。

    • 写 1:主机拉低总线 1µs~15µs,然后释放。

    • 读数据:主机拉低总线 1µs~15µs,然后在 15µs 内采样总线电平。

  3. 数据格式

    • 每个字节按 LSB(最低位优先) 传输。

    • 需支持标准 ROM 命令(如 0xCC 跳过 ROM)和功能命令(如 0x44 启动温度转换)。


2. 你的代码是否算单总线协议?

是的! 你的代码完全符合 DS18B20 的 1-Wire 协议要求:

  • 复位ds18b20_reset() 实现了复位和存在检测。

  • 写操作ds18b20_writebit() 和 ds18b20_writebyte() 严格遵循写时序。

  • 读操作ds18b20_readbit() 和 ds18b20_readbyte() 符合读时序。

  • 命令序列ds18b20_readvalue() 发送了 0xCC(跳过 ROM)和 0x44(启动转换),最后读取数据。


3. 单总线协议的完整流程

以 DS18B20 为例,完整流程包括:

  1. 复位 → 检测设备存在

  2. 发送 ROM 命令(如 0xCC 跳过 ROM)。

  3. 发送功能命令(如 0x44 启动温度转换)。

  4. **

那uart、i2c、spi协议是指按照传感器时序图进行这些操作吗

是的,UART、I2C、SPI 协议的核心也是严格按照传感器或芯片手册的时序图进行通信,但它们与单总线(1-Wire)协议在实现方式、硬件支持和复杂度上有显著区别。以下是详细对比:


1. 协议的本质区别

特性单总线 (1-Wire)UARTI2CSPI
信号线数量1 根(数据线 + 地线)2 根(TX、RX)2 根(SCL、SDA)4 根(SCK、MOSI、MISO、CS)
通信方式半双工(严格时序控制)全双工(异步)半双工(同步)全双工(同步)
时序关键性极高(µs 级延时)中等(波特率匹配)中等(时钟同步)高(时钟边沿采样)
硬件支持需软件模拟或专用控制器多数 MCU 内置硬件 UART多数 MCU 内置硬件 I2C多数 MCU 内置硬件 SPI
典型应用DS18B20 等简单传感器调试串口、模块通信传感器、EEPROM高速设备(Flash、屏幕)

2. 如何理解“按时序图操作”?

(1) 单总线(1-Wire)
  • 完全依赖软件模拟时序(如你的 ds18b20_reset()ds18b20_writebit())。

  • 例:DS18B20 的复位时序:

    c

    复制

    下载

    // 主机拉低 480µs~960µs
    gpio_set_value(gpio, 0);
    udelay(480);
    // 释放总线,等待从机响应
    gpio_set_value(gpio, 1);
(2) UART
  • 依赖波特率(Baud Rate),时序体现在字节格式(起始位、数据位、停止位)。

  • 例:发送字节 0x55(二进制 01010101):

    text

    复制

    下载

    时序图:起始位(0) + 8位数据(LSB优先) + 停止位(1)
(3) I2C
  • 依赖时钟(SCL)同步,时序包括:

    • 起始条件(SCL 高时 SDA 下降沿)。

    • 数据位在 SCL 低电平时变化,高电平时采样。

    • 停止条件(SCL 高时 SDA 上升沿)。

  • 例:读取 I2C 设备(地址 0x50)的寄存器 0x00

    text

    复制

    下载

    时序图:START → 写地址(0x50) → 写寄存器(0x00) → RESTART → 读数据 → STOP
(4) SPI
  • 依赖时钟边沿(SCK)采样,时序包括:

    • 模式(CPOL/CPHA)决定时钟极性和采样边沿。

    • 数据在 SCK 的上升沿或下降沿传输。

  • 例:SPI 模式 0(CPOL=0, CPHA=0):

    text

    复制

    下载

    时序图:SCK 低电平时数据变化,上升沿采样。

3. 关键结论

  1. 单总线

    • 最底层,需完全手动控制 GPIO 时序(如你的代码)。

    • 适用于简单、低速设备(如 DS18B20)。

  2. UART/I2C/SPI

    • 通常由硬件控制器实现,开发者只需配置寄存器(如波特率、时钟频率)。

    • 若没有硬件支持,也可软件模拟(如用 GPIO 模拟 I2C),但复杂度和稳定性较低。

  3. 共同点

    • 无论哪种协议,都必须严格遵循芯片手册的时序图!

    • 差异在于时序的复杂性和硬件支持程度。


4. 实际开发建议

  • 优先使用硬件控制器(如 MCU 的 I2C/SPI 外设),避免软件模拟。

  • 单总线设备
    若需驱动多个 DS18B20,建议改用 Linux 内核的 w1-gpio 驱动(已内置 1-Wire 协议栈)。

  • 调试工具
    用逻辑分析仪抓取时序,对比手册检查是否合规。

LED不需要专用控制器的原因

  1. 简单的工作机制

    • LED是简单的单向导通器件,只需提供适当的电压和电流即可工作

    • 本质上是一个二极管,具有非线性I-V特性

  2. 控制方式简单

    • 可以直接通过开关控制通断

    • 亮度调节可通过PWM(脉宽调制)或改变电流实现

    • 不需要复杂的通信协议

  3. 应用场景

    • 指示用途:简单的开/关状态

    • 照明用途:可能需要恒流驱动,但仍属简单控制

传感器需要总线控制器的原因

  1. 复杂的数据交互

    • 传感器需要发送采集的数据

    • 可能需要接收配置参数或控制指令

    • 双向通信需求

  2. 标准化接口需求

    • 总线控制器实现标准通信协议(I2C, SPI, CAN, RS485等)

    • 解决不同设备间的互操作性问题

    • 处理电气特性匹配(电平转换、阻抗匹配等)

  3. 数据处理功能

    • 数据打包/解包

    • 错误检测与校正

    • 协议转换(如将传感器数据转换为网络协议)

二、总线

之前以及把驱动中设备相关信息从驱动独立出来,放到设备树了,现在由于传感器等的使用需要用到总线,所以直接把总线独立出来。

platform_device                                platform_bus                                platform_driver

       自己注册                                        内核写好了                                 自己编写

platform_device_register                        

注册过时,直接设备树转化

但是要手动配置设备树后转化

一、platform平台虚拟总线(把led、蜂鸣器、emmc、网卡、adc等不需要总线的设备也用总线来简化管理)

(1)、match设备与驱动的匹配方法

        1、设备指定驱动的名字(第一优先级)

        2、驱动中的of_match_table与设备名相同

        3、x86架构下的api匹配模式(适用pc端)

        4、驱动中的ip_table与设备名相同

        5、驱动名与设备名相同

自己写的平台驱动里面有个probe函数,设备跟驱动匹配成功就会执行该函数

remove注销设备

(2)驱动编写

注册设备、重写文件操作结构体、创建设备节点,应用层通过文件操作操作寄存器控制设备

寄存器信息来自跟驱动匹配成功的设备

设备树

驱动

#include <linux/init.h>
#include <linux/fs.h>
#include <linux/module.h>
#include <linux/cdev.h>
#include <linux/cdev.h>
#include<linux/device.h>
#include<asm/io.h>
#include<asm-generic/io.h>
#include<asm/uaccess.h>
#include<linux/string.h>
#include<linux/io.h>
#include<linux/of.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>
#include<linux/miscdevice.h>
#include<linux/platform_device.h>
#include <linux/mutex.h>
static int ledstat;//自定义的led灯状态变量
static int ledgpio = 0;
static struct mutex led_lock;
//文件操作函数
static ssize_t led_read(struct file *fp, char __user *puser, size_t n, loff_t *off)
{int ret;//当用户调用read时,把ledstat的值给用户ret = copy_to_user(puser,&ledstat,sizeof(ledstat));pr_info("read success\n");return sizeof(ledstat);
}
static ssize_t led_write(struct file *fp, const char __user *puser, size_t n, loff_t *off)
{int ret;//拷贝用户调用write写入的数据到ledstatret = copy_from_user(&ledstat,puser,sizeof(ledstat));mutex_lock(&led_lock);//写入1开灯if(ledstat == 1){//置0开灯gpio_set_value(ledgpio,0);}//写入0关灯if(ledstat == 0){//置1关灯gpio_set_value(ledgpio,1);}mutex_unlock(&led_lock);pr_info("write success\n");return ret;
}
static int led_open(struct inode *node, struct file *fp)
{pr_info("open success\n");return 0;
}
static int led_release(struct inode *node, struct file *fp)
{pr_info("release success\n");return 0;
}
//文件操作函数结构体
static struct file_operations fops = {.owner = THIS_MODULE, //计数,表示有几个模块调用文件操作.open = led_open,.release = led_release,  .read = led_read,.write = led_write     
};static struct miscdevice misc_device = {.minor = MISC_DYNAMIC_MINOR,//次设备号申请.name = "misc_led",//设备节点名.fops = &fops,//绑定文件操作函数结构体
};static int led_probe(struct platform_device *pdevice)
{struct device_node  *dtslednode = NULL;//初始化锁mutex_init(&led_lock);//注册混杂设备misc_register(&misc_device);//获取设备树节点dtslednode = pdevice->dev.of_node;//获取引脚编号ledgpio = of_get_named_gpio(dtslednode,"led-gpio",0);//GPIO方向设为输出gpio_direction_output(ledgpio,1);//设备树已初始化为高电平ledstat = 0;pr_info("led_probe success\n");return 0;
}
static int led_remove(struct platform_device *pdevice)
{//释放引脚编号gpio_free(ledgpio);//销毁混杂设备misc_deregister(&misc_device);pr_info("led_remove success\n");return 0;
}//平台设备驱动匹配
static struct of_device_id  led_of_match_table[] = {{.compatible = "pute,puteleds"},{},
};
static struct platform_device_id led_id_table[] = {{.name = "puteleds"},{},
};
static struct platform_driver led_platform_drv = {.driver = {.name = "puteleds",.of_match_table = led_of_match_table,},.id_table = led_id_table,.probe = led_probe,.remove = led_remove,
};//驱动入口内调函数
static int __init led_init(void)
{//向内核注册一个平台驱动,将其绑定到platform虚拟总线上,并触发匹配设备的功能platform_driver_register(&led_platform_drv);pr_info("beep init success\n");return 0;
}//驱动出口内调函数
static void __exit led_exit(void)
{//销毁平台驱动platform_driver_unregister(&led_platform_drv);pr_info("beep exit success\n");return;
}//驱动入口
module_init(led_init);
//驱动出口
module_exit(led_exit);MODULE_LICENSE("GPL");

二、利用gpio单总线通信采集ds18b20传感器

线与特性:只有当都为高电平时表示没有收发数据,只要有一方想要收发数据,就要先拉低电平。

1、初始化:

2、rom操作        0xcc跳过rom

3、存储器操作        0x44   启动温度转换

4、等待转化完成

5、初始化

6、rom操作                0xcc

7、存储器操作        0xbe读前两个字节返回数据,*0.0625即温度

发送0xcc:即发送一个字节,先看看怎么发送一位

write:

 12位默认,即等待750毫秒

read:

延时用delay实现

#include <asm/io.h>
#include <asm/uaccess.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/io.h>
#include <linux/miscdevice.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>
#include <linux/platform_device.h>
#include <linux/string.h>
#include <linux/delay.h>
static int ds18b20state;  //自定义的led灯状态变量
static int ds18b20gpio = 0;
static struct mutex ds18b20_lock;static int ds18b20_reset(void)
{int timeout = 20;//拉低480-960usgpio_set_value(ds18b20gpio, 0);udelay(960);gpio_set_value(ds18b20gpio, 1);udelay(60);//等待低电平到来gpio_direction_input(ds18b20gpio);timeout = 20;while (gpio_get_value(ds18b20gpio) && timeout--);if (gpio_get_value(ds18b20gpio))return -1;//等待高电平到来while (!gpio_get_value(ds18b20gpio));udelay(410);gpio_direction_output(ds18b20gpio, 1);return 0;
}static void ds18b20_writebit(unsigned char data)
{//拉低 > 1usgpio_set_value(ds18b20gpio, 0);udelay(5);if (data)gpio_set_value(ds18b20gpio, 1);udelay(115);gpio_set_value(ds18b20gpio, 1);udelay(3);return;
}static void ds18b20_writebyte(unsigned char byte)
{int i = 0;for (i = 0; i < 8; i++){ds18b20_writebit(byte & 0x01);byte >>= 1;}return;
}static unsigned char ds18b20_readbit(void)
{unsigned char data = 0;//拉低 > 1usgpio_set_value(ds18b20gpio, 0);udelay(5);gpio_set_value(ds18b20gpio, 1);gpio_direction_input(ds18b20gpio);udelay(7);data = gpio_get_value(ds18b20gpio);gpio_direction_output(ds18b20gpio, 1);udelay(50);return data;
}static unsigned char ds18b20_readbyte(void)
{unsigned char byte = 0;int i = 0;for (i = 0; i < 8; i++){if (ds18b20_readbit())byte |= 0x1 << i;}return byte;
}static int ds18b20_readvalue(unsigned long *pvalue)
{int ret = 0;unsigned short th = 0;unsigned short tl = 0;//初始化设备ret = ds18b20_reset();if (-1 == ret)return -1;//跳过ROM(0xcc)ds18b20_writebyte(0xcc);//操作存储器(0x44)ds18b20_writebyte(0x44);//等待转换完成msleep(750);//初始化设备ret = ds18b20_reset();if (-1 == ret)return -1;//跳过ROM(0xcc)ds18b20_writebyte(0xcc);//操作存储器(0xBE)ds18b20_writebyte(0xbe);//读取数据(前2个字节)tl = ds18b20_readbyte();th = ds18b20_readbyte();*pvalue = ((th << 8) | tl); return 0;
}static ssize_t ds18b20_read(struct file *fp, char __user *puser, size_t n, loff_t *off)
{unsigned long ret = 0;unsigned long value = 0;ret = ds18b20_readvalue(&value);if (ret != 0)return -1;ret = copy_to_user(puser, &value, sizeof(value));if (ret)pr_info("copy_to_user failed\n");return sizeof(value);
}static ssize_t ds18b20_write(struct file *fp, const char __user *puser, size_t n,loff_t *off)
{int ret;//拷贝用户调用write写入的数据到ds18b20stateret = copy_from_user(&ds18b20state, puser, sizeof(ds18b20state));mutex_lock(&ds18b20_lock);//写入1开灯if (ds18b20state == 1){//置0开灯gpio_set_value(ds18b20gpio, 0);}//写入0关灯if (ds18b20state == 0){//置1关灯gpio_set_value(ds18b20gpio, 1);}mutex_unlock(&ds18b20_lock);pr_info("write success\n");return ret;
}
static int ds18b20_open(struct inode *node, struct file *fp)
{pr_info("open success\n");return 0;
}
static int ds18b20_release(struct inode *node, struct file *fp)
{pr_info("release success\n");return 0;
}
//文件操作函数结构体
static struct file_operations fops = {.owner = THIS_MODULE,  //计数,表示有几个模块调用文件操作.open = ds18b20_open,.release = ds18b20_release,.read = ds18b20_read,.write = ds18b20_write};static struct miscdevice misc_device = {.minor = MISC_DYNAMIC_MINOR,  //次设备号申请.name = "misc_ds18b20",           //设备节点名.fops = &fops,                //绑定文件操作函数结构体
};static int ds18b20_probe(struct platform_device *pdevice)
{struct device_node *dtslednode = NULL;//初始化锁mutex_init(&ds18b20_lock);//注册混杂设备misc_register(&misc_device);//获取设备树节点dtslednode = pdevice->dev.of_node;//获取引脚编号ds18b20gpio = of_get_named_gpio(dtslednode, "ds18b20-gpio", 0);// GPIO方向设为输出gpio_direction_output(ds18b20gpio, 1);//设备树已初始化为高电平ds18b20state = 0;pr_info("ds18b20_probe success\n");return 0;
}
static int ds18b20_remove(struct platform_device *pdevice)
{//释放引脚编号gpio_free(ds18b20gpio);//销毁混杂设备misc_deregister(&misc_device);pr_info("ds18b20_remove success\n");return 0;
}//平台设备驱动匹配
static struct of_device_id ds18b20_of_match_table[] = {{.compatible = "pute,puteds18b20"},{},
};
static struct platform_device_id ds18b20_id_table[] = {{.name = "puteds18b20"},{},
};
static struct platform_driver ds18b20_platform_drv = {.driver ={.name = "puteds18b20",.of_match_table = ds18b20_of_match_table,},.id_table = ds18b20_id_table,.probe = ds18b20_probe,.remove = ds18b20_remove,
};//驱动入口内调函数
static int __init ds18b20_init(void)
{//向内核注册一个平台驱动,将其绑定到platform虚拟总线上,并触发匹配设备的功能platform_driver_register(&ds18b20_platform_drv);pr_info("ds18b20 init success\n");return 0;
}//驱动出口内调函数
static void __exit ds18b20_exit(void)
{//销毁平台驱动platform_driver_unregister(&ds18b20_platform_drv);pr_info("ds18b20 exit success\n");return;
}//驱动入口
module_init(ds18b20_init);
//驱动出口
module_exit(ds18b20_exit);MODULE_LICENSE("GPL");
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>int main(void)
{int fd = 0;unsigned long value = 0;fd = open("/dev/misc_ds18b20", O_RDWR);if (-1 == fd){perror("fail to open");return -1;}while (1){read(fd, &value, sizeof(value));printf("value = %.2lf\n", value * 0.0625);sleep(1);}close(fd);return 0;
}

dht11

#include <asm/io.h>
#include <asm/uaccess.h>
#include <linux/cdev.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/io.h>
#include <linux/miscdevice.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>
#include <linux/platform_device.h>
#include <linux/string.h>
extern void msleep(unsigned int msecs);
static int dht11state;  //自定义的led灯状态变量
static int dht11gpio = 0;
static struct mutex dht11_lock;static void dht11_send_start(void)
{   int ret = 0;//拉低 > 18msret = gpio_direction_output(dht11gpio, 1);if (ret)pr_info("gpio_direction_output failed\n");gpio_set_value(dht11gpio, 0);msleep(20);//拉高gpio_set_value(dht11gpio, 1);return;
}static int dht11_recv_reply(void)
{int ret = 0;int timeout = 0;ret = gpio_direction_input(dht11gpio);if (ret)pr_info("gpio_direction_input failed\n");while (gpio_get_value(dht11gpio) && timeout < 200) {udelay(5);timeout++;}if (timeout >= 200)return -1;while (!gpio_get_value(dht11gpio));while (gpio_get_value(dht11gpio));return 0;
}static unsigned char dht11_recv_byte(void)
{unsigned char data = 0;int ret = 0;int cnt = 0;int i = 0;ret = gpio_direction_input(dht11gpio);if (ret)pr_info("gpio_direction_input failed\n");for (i = 7; i >= 0; i--) {cnt = 0;while (!gpio_get_value(dht11gpio));while (gpio_get_value(dht11gpio)) {udelay(5);cnt++; }if (cnt > 8)data |= 0x1 << i; }return data;   
}static ssize_t dht11_read(struct file *fp, char __user *puser, size_t n, loff_t *off)
{int ret = 0;long nret = 0;unsigned char data[5] = {0};dht11_send_start();ret = dht11_recv_reply();if (-1 == ret)return -1;data[0] = dht11_recv_byte();data[1] = dht11_recv_byte();data[2] = dht11_recv_byte();data[3] = dht11_recv_byte();data[4] = dht11_recv_byte();ret = gpio_direction_output(dht11gpio, 1);if (ret)pr_info("gpio_direction_output failed\n");if (data[0] + data[1] + data[2] + data[3] != data[4])return -2;nret = copy_to_user(puser, data, 4);if (nret)pr_info("copy_to_user failed\n");return 4;
}////////////////////////////////////////////////static ssize_t dht11_write(struct file *fp, const char __user *puser, size_t n,loff_t *off)
{int ret;//拷贝用户调用write写入的数据到dht11stateret = copy_from_user(&dht11state, puser, sizeof(dht11state));mutex_lock(&dht11_lock);//写入1开灯if (dht11state == 1){//置0开灯gpio_set_value(dht11gpio, 0);}//写入0关灯if (dht11state == 0){//置1关灯gpio_set_value(dht11gpio, 1);}mutex_unlock(&dht11_lock);pr_info("write success\n");return ret;
}
static int dht11_open(struct inode *node, struct file *fp)
{pr_info("open success\n");return 0;
}
static int dht11_release(struct inode *node, struct file *fp)
{pr_info("release success\n");return 0;
}
//文件操作函数结构体
static struct file_operations fops = {.owner = THIS_MODULE,  //计数,表示有几个模块调用文件操作.open = dht11_open,.release = dht11_release,.read = dht11_read,.write = dht11_write};static struct miscdevice misc_device = {.minor = MISC_DYNAMIC_MINOR,  //次设备号申请.name = "misc_dht11",         //设备节点名.fops = &fops,                //绑定文件操作函数结构体
};static int dht11_probe(struct platform_device *pdevice)
{struct device_node *dtslednode = NULL;//初始化锁mutex_init(&dht11_lock);//注册混杂设备misc_register(&misc_device);//获取设备树节点dtslednode = pdevice->dev.of_node;//获取引脚编号dht11gpio = of_get_named_gpio(dtslednode, "ds18b20-gpio", 0);// GPIO方向设为输出gpio_direction_output(dht11gpio, 1);//设备树已初始化为高电平dht11state = 0;pr_info("dht11_probe success\n");return 0;
}
static int dht11_remove(struct platform_device *pdevice)
{//释放引脚编号gpio_free(dht11gpio);//销毁混杂设备misc_deregister(&misc_device);pr_info("dht11_remove success\n");return 0;
}//平台设备驱动匹配
static struct of_device_id dht11_of_match_table[] = {{.compatible = "pute,puteds18b20"},{},};
static struct platform_device_id dht11_id_table[] = {{.name = "puteds18b20"},{},
};
static struct platform_driver dht11_platform_drv = {.driver ={.name = "puteds18b20",.of_match_table = dht11_of_match_table,},.id_table = dht11_id_table,.probe = dht11_probe,.remove = dht11_remove,
};//驱动入口内调函数
static int __init dht11_init(void)
{//向内核注册一个平台驱动,将其绑定到platform虚拟总线上,并触发匹配设备的功能platform_driver_register(&dht11_platform_drv);pr_info("dht11 init success\n");return 0;
}//驱动出口内调函数
static void __exit dht11_exit(void)
{//销毁平台驱动platform_driver_unregister(&dht11_platform_drv);pr_info("dht11 exit success\n");return;
}//驱动入口
module_init(dht11_init);
//驱动出口
module_exit(dht11_exit);MODULE_LICENSE("GPL");
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>int main(void)
{int fd = 0;unsigned char data[4] = {0};char hum[32] = {0};char temp[32] = {0};fd = open("/dev/misc_dht11", O_RDWR);if (-1 == fd){perror("fail to open");return -1;}while (1){read(fd, data, sizeof(data));sprintf(hum, "%d.%d", data[0], data[1]);sprintf(temp, "%d.%d", data[2], data[3]);printf("hum = %.2lf, temp = %.2lf\n", atof(hum), atof(temp));sleep(2);}close(fd);return 0;
}

前瞻结束

19.platform驱动框架

1.概念

        Linux设备驱动非常重视软件的可重用性和跨平台能力。如果我们多个平台需要设备A,而A在不同板子上的硬件信息不同,所以驱动中就需要写很多if else 来区分不同的硬件平台,来做不同的硬件操作。

这种操作非常麻烦,所以我们想将设备信息从驱动中剥离开来,让驱动拿到硬件平台信息。所以逐渐衍生出来了如下模型。

        设备:存放硬件平台的寄存器信息及地址信息(后来衍生出了设备树概念)

        总线:负责对设备与驱动进行管理及匹配

        驱动:通过总线与设备按照规则匹配成功后,拿到设备信息,并进行对设备的操作。

        相比如I2c、 SPI这些有实际物理总线的硬件,芯片上面有一些外设之间没有通过总线连接,比如ADC、网卡,如果我们想将这些硬件驱动也通过这种模型设计完成,内核设计者们为我们设计了platform总线架构,也称为虚拟平台总线。

结构中:

        设备类型为 platform_device 类型

        总线类型为 platform_bus_type 类型

        驱动类型为 platform_driver 类型

设备端包含了物理平台寄存器信息及中断信息,这部分信息目前位于设备树中,内核自动将其转换为platform_device类型,总线负责将设备与驱动进行匹配,匹配成功执行驱动中的probe函数指针指向的接口来加载驱动。

相比如之前驱动通过设备树手动获得节点数据,这种总线形式能够自动匹配并获得节点数据。两者的区别:

特性Platform 架构直接解析设备树
标准化程度符合内核驱动模型依赖自定义实现
热插拔支持自动响应设备动态变化仅支持静态设备
资源管理安全性自动分配/释放,防冲突手动管理,易泄漏或冲突
代码可维护性硬件变动仅需修改设备树硬件变动需修改驱动代码
适用场景通用设备、需热插拔的设备固定基础设施(如中断控制器)

函数接口:

platform_driver_register向内核注册一个平台驱动,将其绑定到platform虚拟总线上,并触发匹配设备的功能
platform_driver_unregister从内核注销平台驱动,解除与所有绑定设备的关联,并触发驱动的remove回调释放资源
module_platform_driver用于自动生成驱动注册/注销的模版代码,简化平台驱动模块化加载
platform_get_resource从平台设备中提取硬件资源信息
platform_get_irq从平台设备中获取中断资源
platform_set_drvdata存储驱动私有数据
platform_get_drvdata读取驱动私有数据
platform_driver_register(drv);
void platform_driver_unregister(struct platform_driver *drv);
module_platform_driver(__platform_driver);
struct resource *platform_get_resource(struct platform_device *dev, unsigned int
type, unsigned int num);
int platform_get_irq(struct platform_device *dev, unsigned int num);
static inline void platform_set_drvdata(struct platform_device *pdev, void
*data);
static inline void *platform_get_drvdata(const struct platform_device *pdev);

20.I2C设备驱动

采用传统的字符设备驱动编写方式在处理总线编程时非常复杂,假设有M个控制器,每个控制器上可能接不同的N个外设,那么我们需要编写的驱动将是M*N个,如下图所示:

这样显然是不能被接受的, M*N是一种强耦合的设计方式,所以我们提炼出一个中间层,让M与中间层耦合, N也与中间层耦合,所以就有了如下模型:

那么我们逐渐将主机控制器驱动与外设传感器驱动进行分离,这样主机控制器驱动与外设控制器驱动互不关心,外设可以通过访问核心层的通用API进行数据传输,主机和外设之间可以进行任意组合。

所以后续的I2c子系统框架、 SPI子系统框架、 USB子系统框架等总线驱动都是这种模型。

函数名功能
i2c_add_driver向I2c子系统注册驱动,并触发设备匹配流程
i2c_del_driver注销I2c驱动,释放资源并解除设备绑定
i2c_add_driver(driver);
void i2c_del_driver(struct i2c_driver *driver)

21.SPI设备驱动

SPI驱动和I2c驱动是类似的,具体实现如下图所示:

函数名功能
spi_register_driver向SPI子系统注册驱动,实现设备匹配
spi_unregister_driver注销SPI驱动,清理资源
int spi_register_driver(struct spi_driver *sdrv);
static inline void spi_unregister_driver(struct spi_driver *sdrv)
http://www.xdnf.cn/news/1173745.html

相关文章:

  • Java 爬虫实战指南:获取淘宝商品详情
  • Nacos 封装与 Docker 部署实践
  • STP 的原理
  • 《计算机网络》实验报告六 电子邮件
  • string类
  • 深度智能 基座跃迁 | 鸿道Intewell,面向“AI+智造”的新型工业操作系统
  • OpenHarmony BUILD.gn中执行脚本
  • 论文笔记:Tuning Language Models by Proxy
  • 简单理解现代Web应用架构:从简单到企业级
  • 解决Spring事务中RPC调用无法回滚的问题
  • 使用idea 将一个git分支的部分记录合并到git另一个分支
  • Elasticsearch(ES)安装
  • 系统架构师:软件工程-思维导图
  • 通用表格识别技术的应用,深刻改变人们处理表格数据的方式
  • 【读代码】Facebook Denoiser:开源端到端语音降噪系统原理与实战
  • 红宝书单词学习笔记 list 76-100
  • 开源 Arkts 鸿蒙应用 开发(十)通讯--Http数据传输
  • Oracle物化视图详解
  • Linux权限机制:设计哲学、实现原理与安全实践
  • 算法->两正方形共占的面积
  • 【Redis】在Ubentu环境下安装Redis
  • docker的镜像与推送
  • 2025最新Mybatis-plus教程(二)
  • Packmol聚合物通道模型建模方法
  • 半导体 CIM(计算机集成制造)系统
  • 高亮匹配关键词样式highLightMatchString、replaceHTMLChar
  • Google DeepMind发布MoR架构:50%参数超越传统Transformer,推理速度提升2倍
  • OpenLayers 快速入门(七)矢量数据
  • Linux 环境下安装 MySQL 8.0.34 二进制 详细教程 附docker+k8s启动
  • 亚马逊广告优化技巧:如何减少预算浪费