锐单电子商城 , 一站式电子元器件采购平台!
  • 电话:400-990-0325

Linux下的驱动学习笔记(2)

时间:2022-09-27 04:30:01 2c公形引脚插头线对线连接器单芯f头连接器hf1变送传感器传感器pec09hf角插接连接器opb620传感器

笔记目录

    • 四. I2C从设备驱动编程
      • 1. 重要结构体
      • 2. 创建i2c_client
      • 3.温湿度传感器驱动编程思路
      • 4.温湿度传感器应用层层
    • 五. I2C-T>ools使用
      • 1. 交叉编译i2c-tools
      • 2. 使用命令
    • 六. 编写I2C_Adapter驱动
      • 1. 设备树
      • 2. platform_driver
      • 3. i2c_apdater
    • 七. 找到相应的设备树节点
  • 十三、 USB协议
    • 一. USB基础知识
      • 1. USB简介
      • 2. USB不同版本
      • 3. USB拓扑结构(hub)
      • 4. USB传输方式
      • 5. USB设备的逻辑组织
    • 二. USB协议
      • 1. USB描述符(重点,面试要问)
      • 2、USB描述符结构体(全在)USB设备的EEPROM里)
      • 3. USB数据包类型
      • 4. USB传输类型
      • 5.USB驱动架构
      • 6.主控制器驱动功能
      • 7.端点endpoint
      • 8.请求块urb
      • 9.管道
      • 10.驱动函数
    • 三、USB鼠标驱动编程思路
      • 1. 注册USB接口
      • 2. 上一个反操作
      • 3. 自定义mouse结构体,定义总线,定义驱动结构体
      • 4. 完成进入总线匹配probe设备、接口、端口、mouse、input
      • 5. 打开设备节点,异步提交urb
      • 6. 反操作
      • 7. 事件驱动提交数据
    • 四. 设备树
      • 1. USBH 控制器节点信息
      • 2. 配置 PHY 控制器
  • 十四、SPI协议
    • 一.SPI硬件知识
      • 1. 硬件连接
      • 2. 传输示例
      • 3. SPI模式
    • 二. Linux SPI驱动框架
    • 三. SPI从设备驱动
    • 四. SPI_OLED模块操作方法
      • 1. OLED操作原理
      • 2. 显存和像素
      • 3. 显示寻址模式
      • 4. 初始化
      • 5. 设置地址
      • 6. 写入数据
    • 五. 自定义总线
    • 六.CUBEMX生成设备树
    • 七.移位寄存器M74HC595级联spi驱动数码管(驱动层)
    • 八.移位寄存器M74HC595级联spi驱动数码管(应用层)
  • 十五、PCI外围设备互联
    • 一.PCI配置寄存器
    • 二.取得设备的io或mem
  • 十六、字符设备
    • 一. 模块
    • 二. 由简单字符设备驱动
    • 三. Linux中的重要结构体关系
    • 四. 杂项设备驱动
  • 十七、系统移植(未完)
          • 一. 一般过程
  • 十八、drm驱动模型(未完)
    • 一. 应用编程
    • 二. 重要结构体
  • 附录
  • 一、配置busybox的语句

四. I2C从设备驱动编程

1. 重要结构体

struct i2c_driver { 
          int (*probe)(struct i2c_client *client, const struct i2c_device_id *id);  int (*remove)(struct i2c_client *client);  struct device_driver driver;  const struct i2c_device_id *id_table;
};
int i2c_register_driver(struct module *owner, struct i2c_driver *driver)
void i2c_del_driver(struct i2c_driver *driver)

2. 创建i2c_client

1> 使用设备树方式

参考:Documentation/devicetree/bindings/i2c/i2c-stm32.txt
修改linux-5.4.31/arch/arm/boot/dts/stm32mp157a-fsmp1a.dts文件
&i2c1 { 
        
    pinctrl-names = "default", "sleep";
    pinctrl-0 = <&i2c1_pins_b>;
    pinctrl-1 = <&i2c1_pins_sleep_b>;
    i2c-scl-rising-time-ns = <100>;
    i2c-scl-falling-time-ns = <7>;
    status = "okay";
    /delete-property/dmas;
    /delete-property/dma-names;
    si7006@40{ 
        
       compatible = "stm32mp157,i2c_si7006";
       reg = <0x40>;
    };
};
//初始化引脚
修改linux-5.4.31/arch/arm/boot/dts/stm32mp15-pinctrl.dtsi
i2c1_pins_sleep_b: i2c1-1 { 
        
    pins { 
        
    pinmux = <STM32_PINMUX('F', 14, ANALOG)>, /* I2C1_SCL */
             <STM32_PINMUX('F', 15, ANALOG)>; /* I2C1_SDA */
    };
};

2> 使用代码方式

static struct i2c_client *si7006_client;

static const unsigned short addr_list[] = { 
         0x40, 0x50, I2C_CLIENT_END};

static int i2c_si7006_dev_init(void)
{ 
        
	struct i2c_adapter *i2c_adap;
	struct i2c_board_info si7006_info;

	memset(&si7006_info, 0, sizeof(struct i2c_board_info));	
	strlcpy(si7006_info.type, "i2c_si7006", I2C_NAME_SIZE);

	i2c_adap = i2c_get_adapter(0);
	si7006_client = i2c_new_probed_device(i2c_adap, &si7006_info, addr_list, NULL);
	i2c_put_adapter(i2c_adap);

	if (si7006_client)
		return 0;
	else
		return -ENODEV;
}
static void i2c_si7006_dev_exit(void)
{ 
        
	i2c_unregister_device(si7006_client);
}
* i2c_new_device
* i2c_new_probed_device(用这个方法前要先使能引脚)
* i2c_register_board_info
* 内核没有`EXPORT_SYMBOL(i2c_register_board_info)`
* 使用这个函数的驱动必须编进内核里去

3> 使用命令行方式

echo i2c_si7006 0x40 > /sys/bus/i2c/devices/i2c-0/new_device
echo 0x40 > /sys/bus/i2c/devices/i2c-0/delete_device

3.温湿度传感器驱动编程思路

  1. 注册I2C设备

    static int __init i2c_si7006_drv_init(void)
    { 
              //注册I2C设备
    	return i2c_register_driver(THIS_MODULE, &i2c_si7006_drv);
    }
    
  2. 创建重要结构体

    //创建i2c从机结构体指针(传感器)
    struct i2c_client *si7006_client;
    //文件操作
    const struct file_operations si7006_fops = { 
              
    	.owner		= THIS_MODULE,	
    	.unlocked_ioctl	= i2c_si7006_drv_ioctl,	//应用层用到switch调用drv_ioctl驱动接口
    };
    //杂项驱动
    struct miscdevice misc = { 
              
    	.minor = 199,
    	.name = "i2c_si7006",
    	.fops = &si7006_fops,
    };
    //创建I2C总线
    const struct of_device_id i2c_si7006_table[] = { 
              
    	{ 
               .compatible = "stm32mp157,i2c_si7006"},
    	{ 
               },
    };
    //I2C驱动结构体 
    struct i2c_driver i2c_si7006_drv = { 
              
    	.probe = i2c_si7006_drv_probe,
    	.remove = i2c_si7006_drv_remove,
    	.driver = { 
              
    		.name = "stm32_i2c_si7006",
    		.of_match_table = i2c_si7006_table,
    	},
    };
    
  3. probe函数

    int i2c_si7006_drv_probe(struct i2c_client *client, const struct i2c_device_id *id_table)
    { 
              //注册杂项设备以及匹配从机
    	int ret;
    	printk("----------------%s-----------------\n",__FUNCTION__);
    	si7006_client = client;
    	ret = misc_register(&misc);
    	return 0;
    }
    
  4. remove函数(反操作)

    int i2c_si7006_drv_remove(struct i2c_client *client)
    { 
              
    	printk("----------------%s-----------------\n",__FUNCTION__);
    	misc_deregister(&misc);
    	return 0;
    }
    
  5. 读取温度在这里插入图片描述

    //入口参数@addr:从机地址,@reg:从机寄存器地址,@buffer:保存读取数据,@length:reg/buffer的长度
    int read_temperature(struct i2c_client *client)
    { 
              
    	int ret;
        unsigned char rx_buf[2];
        unsigned char tx_buf[] = { 
              0xE3};
    	struct i2c_msg msg[2];//一个i2c_msg结构体代表着一次单方向的传输
    	int temp_code;
    	msg[0].addr = client->addr;
    	msg[0].flags = 0;(写信号)
    	msg[0].buf = tx_buf;
    	msg[0].len = sizeof(tx_buf)/sizeof(tx_buf[0]);
    
    	msg[1].addr = client->addr;
    	msg[1].flags = 1;(读信号)
    	msg[1].buf = rx_buf;
    	msg[1].len = sizeof(rx_buf)/sizeof(rx_buf[0]);//i2c_transfer执行上面I2C结构体的操作
    	ret = i2c_transfer(client->adapter, msg, sizeof(msg)/sizeof(msg[0]));
    	if(ret < 0)
    	{ 
              	
    		printk("Fail to i2c transfer\n");		
    		return ret;
    	}
    	temp_code = rx_buf[0] << 8 | rx_buf[1];//rx_buf[0]先收到高位数据(例:十位),rx_buf[1]后收到低位(个位)
    	//temperature = (175.72 * temp_code)/ 65536 - 46.85;
    	return temp_code
    }
    
  6. 读取相对湿度

    int read_humidity(struct i2c_client *client)
    { 
              
    	int ret;
        unsigned char rx_buf[2];
        unsigned char tx_buf[] = { 
              0xE5};
    	struct i2c_msg msg[2];
    	int hum_code;
    	msg[0].addr = client->addr;
    	msg[0].flags = 0;
    	msg[0].buf = tx_buf;
    	msg[0].len = sizeof(tx_buf)/sizeof(tx_buf[0]);
    
    	msg[1].addr = client->addr;
    	msg[1].flags = 1;
    	msg[1].buf = rx_buf;
    	msg[1].len = sizeof(rx_buf)/sizeof(rx_buf[0]);
    	ret = i2c_transfer(client->adapter, msg, sizeof(msg)/sizeof(msg[0]));
    	if(ret < 0)
    	{ 
              	
    		printk("Fail to i2c transfer\n");		
    		return ret;
    	}
    	hum_code = rx_buf[0] << 8 | rx_buf[1];
    	//humidity = (125 * hum_code)/ 65536 - 6;
    	return hum_code;
    }
    
  7. ioctl驱动接口

    long i2c_si7006_drv_ioctl(struct file * filp, unsigned int cmd, unsigned long arg)
    { 
              
    	int ret;	
    	unsigned int temp;	
    	unsigned int hum;			
    	switch(cmd)
    	{ 
              		
    	case SI7006_GET_TEMP:			
    		temp = read_temperature(si7006_client);			
    		ret = copy_to_user((void __user *)arg, &temp, sizeof(temp));			
    		if(ret > 0)
    		{ 
              				
    			printk("Fail to copy to user\n");				
    			return -EFAULT;		
    		}			
    		break;	   
    	case SI7006_GET_HUM:	   		
    		hum = read_humidity(si7006_client);			
    		ret = copy_to_user((void __user *)arg, &hum, sizeof(hum));	
    		if(ret > 0)
    		{ 
              				
    			printk("Fail to copy to user\n");	
    			return -EFAULT;		
    		}			
    		break;				
    	default:	
    		printk("unKnown cmd!\n");			
    		break;
    	}		
    	return 0;
    }
    

4.温湿度传感器应用层

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
//定义获得温度以及相对湿度的特定宏
#define SI7006_GET_TEMP _IOR('S',0x00,short) 
#define SI7006_GET_HUM _IOR('S',0x01,short)

int main(void)
{ 
        
	int fd;
	int ret;
	float temperature;
	int value;
	int humidity;
	//打开设备节点
	fd = open("/dev/i2c_si7006", O_RDWR);
	if(fd < 0)
	{ 
        
		perror("open");
		exit(1);
	}

	while(1)
	{ 
        
		//调用驱动层接口获取i2c温度储存到value里
		ret = ioctl(fd,SI7006_GET_TEMP,(unsigned long)&value);		
		if(ret < 0)
		{ 
        			
			perror("Fail to ioctl");			
			return -1;		
		}
		//数学转换
		temperature = (175.72 * (unsigned short)value)/ 65536 -  46.85;
		printf("Temperature:%.2f\n",temperature);		
		sleep(1);
		//调用驱动层接口获取i2c相对湿度储存到value里
		ret = ioctl(fd,SI7006_GET_HUM,(unsigned long)&value);		
		if(ret < 0)
		{ 
        			
			perror("Fail to ioctl");			
			return -1;	
		}
		//数学转换
		humidity = (125 * (unsigned short)value)/ 65536 - 6;		
		printf("Humidity:%d%%\n",humidity);		
		sleep(1);
	}
	close(fd);
}

五. I2C-T>ools使用

  Linux内核里面有一个通用的i2c驱动程序—i2c-dev.c,可以下载一个开源的i2c应用—i2c-tools-4.2.tar.xz

  i2c-tools-4.2.tar.xz交叉编译完毕后会有很多命令<可执行程序>,运行这些命令<可执行程序>,会调用i2c通用驱动程序i2c-dev.c,实现对从设备的操作!

  因此i2c从设备的操作,可以通过命令行来完成,不用自己写驱动程序和应用程序也可以!

1. 交叉编译i2c-tools

farsight@ubuntu:/opt/stm32_sdk$ vi environment-setup-cortexa7t2hf-neon-vfpv4-ostl-linux-gnueabi
export ARM_CC="arm-ostl-linux-gnueabi-gcc -mthumb -mfpu=neon-vfpv4 -mfloat-abi=hard -mcpu=cortex-a7 --sysroot=$SDKTARGETSYSROOT"
export ARM_AR=arm-ostl-linux-gnueabi-ar
export ARM_STRIP=arm-ostl-linux-gnueabi-strip

修改i2c-tools-4.2/Makefile
CC      = $(ARM_CC)
AR      = $(ARM_AR)
STRIP   = $(ARM_STRIP)
执行make即可

* 执行make时,是动态链接,需要把libi2c.so也放到板子上
* 想静态链接的话,执行:make USE_STATIC_LIB=1

把i2c-tools-4.2/tools中生成的命令拷贝到开发板即可!

2. 使用命令

<1> i2cdetect — I2C检测

1> 列出当前的I2C Adapter(或称为I2C Bus、I2C Controller)
# i2cdetect -l
i2c-1   i2c             STM32F7 I2C(0x40013000)                 I2C adapter
i2c-2   i2c             STM32F7 I2C(0x5c002000)                 I2C adapter
i2c-0   i2c             STM32F7 I2C(0x40012000)                 I2C adapter

2> 打印某个I2C Adapter的Functionalities, I2CBUS为0、1、2等整数
# i2cdetect -F 0
Functionalities implemented by /dev/i2c-0:
I2C                              yes
SMBus Quick Command              yes
SMBus Send Byte                  yes
SMBus Receive Byte               yes
SMBus Write Byte                 yes
SMBus Read Byte                  yes
SMBus Write Word                 yes
SMBus Read Word                  yes
SMBus Process Call               yes
SMBus Block Write                yes
SMBus Block Read                 yes
SMBus Block Process Call         yes
SMBus PEC                        yes
I2C Block Write                  yes
I2C Block Read                   yes

3> 看看有哪些I2C设备, I2CBUS为0、1、2等整数
// --表示没有该地址对应的设备, UU表示有该设备并且它已经有驱动程序,
// 数值表示有该设备但是没有对应的设备驱动
# i2cdetect -y -a 0
     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
00: 00 -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- 1e --
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
40: UU -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
50: -- -- -- -- -- -- -- 57 -- -- -- -- -- -- -- --
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
70: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --

<2> i2cget — I2C读

# i2cget
Usage: i2cget [-f] [-y] [-a] I2CBUS CHIP-ADDRESS [DATA-ADDRESS [MODE]]
  I2CBUS is an integer or an I2C bus name
  ADDRESS is an integer (0x03 - 0x77, or 0x00 - 0x7f if -a is given)
  MODE is one of:
    b (read byte data, default)
    w (read word data)
    c (write byte/read byte)
    Append p for SMBus PEC
1> 读一个字节 
I2CBUS为0、1、2等整数, 表示I2C Bus; CHIP-ADDRESS表示设备地址
i2cget -f -y I2CBUS CHIP-ADDRESS
// 读某个地址上的一个字节: 
//    I2CBUS为0、1、2等整数, 表示I2C Bus
//    CHIP-ADDRESS表示设备地址
//    DATA-ADDRESS: 芯片上寄存器地址
//    MODE:有2个取值, b-使用`SMBus Read Byte`先发出DATA-ADDRESS, 再读一个字节, 中间无P信号
//                   c-先write byte, 在read byte,中间有P信号 
i2cget -f -y I2CBUS CHIP-ADDRESS DATA-ADDRESS MODE  

2> 读某个地址上的2个字节
//    I2CBUS为0、1、2等整数, 表示I2C Bus
//    CHIP-ADDRESS表示设备地址
//    DATA-ADDRESS: 芯片上寄存器地址
//    MODE:w-表示先发出DATA-ADDRESS,再读2个字节
i2cget -f -y I2CBUS CHIP-ADDRESS DATA-ADDRESS MODE  

<3> i2cset — I2C写

# i2cset
Usage: i2cset [-f] [-y] [-m MASK] [-r] [-a] I2CBUS CHIP-ADDRESS DATA-ADDRESS [VALUE] ... [MODE]
  I2CBUS is an integer or an I2C bus name
  ADDRESS is an integer (0x03 - 0x77, or 0x00 - 0x7f if -a is given)
  MODE is one of:
    c (byte, no value)
    b (byte data, default)
    w (word data)
  i (I2C block data)
    s (SMBus block data)
    Append p for SMBus PEC
锐单商城拥有海量元器件数据手册IC替代型号,打造电子元器件IC百科大全!

相关文章