Linux驱动开发12 IIC驱动

这篇具有很好参考价值的文章主要介绍了Linux驱动开发12 IIC驱动。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

IIC总线驱动+IIC设备驱动(驱动分割分离分层思想)     

我们不需要写适配器,只需要写设备驱动  

        I2C 是很常用的一个串行通信接口,用于连接各种外设、传感器等器件,在裸机篇已经对
I.MX6U I2C 接口做了详细的讲解。本章我们来学习一下如何在 Linux 下开发 I2C 接口器件驱动,重点是学习 Linux 下的 I2C 驱动框架,按照指定的框架去编写 I2C 设备驱动。
Linux驱动开发12 IIC驱动

        I2C 总线驱动重点是 I2C 适配器(也就是 SOC I2C 接口控制器)驱动,这里要用到 两个重要的数据结构:i2c_adapter i2c_algorithmLinux 内核将 SOC I2C 适配器(控制器) 抽象成i2c_adapteri2c_adapter 结构体定义在 include/linux/i2c.h 文件中。

        Linux驱动开发12 IIC驱动

 =========================================================================

I2C 设备驱动重点关注两个数据结构: i2c_client 和 i2c_driver ,根据总线、设备和驱动模型,
I2C 总线上一小节已经讲了。还剩下设备和驱动, i2c_client 就是描述设备信息的, i2c_driver 描述驱动内容,类似于 platform_driver
Linux驱动开发12 IIC驱动

Linux驱动开发12 IIC驱动

1、对于我们 I2C 设备驱动编写人来说,重点工作就是构建 i2c_driver,构建完成以后需要向 Linux 内核注册这个 i2c_driveri2c_driver 注册函数为 int i2c_register_driver

另外 i2c_add_driver 也常常用于注册 i2c_driver i2c_add_driver 是一个宏,定义如下:
示例代码 61.1.2.3 i2c_add_driver 宏
587 #define i2c_add_driver ( driver ) \
588 i2c_register_driver ( THIS_MODULE , driver )
i2c_add_driver 就是对 i2c_register_driver 做了一个简单的封装,只有一个参数,就是要注册
i2c_driver
2、注销 I2C 设备驱动的时候需要将前面注册的 i2c_driver 从 Linux 内核中注销掉,需要用到i2c_del_driver 函数,此函数原型如下:
void i2c_del_driver(struct i2c_driver *driver)
函数参数和返回值含义如下:
driver :要注销的 i2c_driver
返回值:
=========================================================================
Linux驱动开发12 IIC驱动
在IIC1 上 接了 AP3216C
修改设备树,我们不能在imx6ull.dtsi中修改,只能在imx6ull-alientek-emmc.dts中修改,
从下列代码中可以看出i2c1下添加了ap3216。i2c2下添加了codec等一系列设备
Linux驱动开发12 IIC驱动

 

&i2c1 {
	clock-frequency = <100000>;
	pinctrl-names = "default";
	pinctrl-0 = <&pinctrl_i2c1>;
	status = "okay";

	ap3216c@1e {
		compatible = "alientek,ap3216c";
		reg = <0x1e>;
	};
};

&i2c2 {
	clock_frequency = <100000>;
	pinctrl-names = "default";
	pinctrl-0 = <&pinctrl_i2c2>;
	status = "okay";

	codec: wm8960@1a {
		compatible = "wlf,wm8960";
		reg = <0x1a>;
		clocks = <&clks IMX6UL_CLK_SAI2>;
		clock-names = "mclk";
		wlf,shared-lrclk;
	};

	ov5640: ov5640@3c {
		compatible = "ovti,ov5640";
		reg = <0x3c>;
		pinctrl-names = "default";
		pinctrl-0 = <&pinctrl_csi1>;
		clocks = <&clks IMX6UL_CLK_CSI>;
		clock-names = "csi_mclk";
		pwn-gpios = <&gpio_spi 6 1>;
		rst-gpios = <&gpio_spi 5 0>;
		csi_id = <0>;
		mclk = <24000000>;
		mclk_source = <0>;
		status = "disabled";
		port {
			ov5640_ep: endpoint {
				remote-endpoint = <&csi1_ep>;
			};
		};
	};

	/* zuozhongkai FT5406/FT5426 */
	ft5426: ft5426@38 {
		compatible = "edt,edt-ft5426","edt,edt-ft5406";
		reg = <0x38>;
		pinctrl-names = "default";
		pinctrl-0 = <&pinctrl_tsc
					&pinctrl_tsc_reset >; 
		interrupt-parent = <&gpio1>; 
		interrupts = <9 0>; 
		reset-gpios = <&gpio5 9 GPIO_ACTIVE_LOW>;  
		interrupt-gpios = <&gpio1 9 GPIO_ACTIVE_LOW>; 
		status = "okay";
	};

	gt9147:gt9147@14 {
		compatible = "goodix,gt9147", "goodix,gt9xx";
		reg = <0x14>;
		pinctrl-names = "default";
		pinctrl-0 = <&pinctrl_tsc
					&pinctrl_tsc_reset >; 
		interrupt-parent = <&gpio1>; 
		interrupts = <9 0>; 
		reset-gpios  = <&gpio5 9 GPIO_ACTIVE_LOW>;
		interrupt-gpios = <&gpio1 9 GPIO_ACTIVE_LOW>; 
		status = "disable";  /* 如果需要改为okay */
	};

	/* zuozhongkai sill902x,如果需要HDMI就将status改为okay即可  */
	/*
	sii902x: sii902x@39 {
        compatible = "SiI,sii902x";
        pinctrl-names = "default";
        pinctrl-0 = <&pinctrl_sii902x>;
        interrupt-parent = <&gpio1>;
        interrupts = <9 IRQ_TYPE_EDGE_FALLING>;
		irq-gpios = <&gpio1 9 GPIO_ACTIVE_LOW>;
        mode_str = "1280x720M@60";
        bits-per-pixel = <16>;
        resets = <&sii902x_reset>;
        reg = <0x39>;
        status = "disable"; 
    };*/
};

Linux驱动开发12 IIC驱动

Linux驱动开发12 IIC驱动 Linux驱动开发12 IIC驱动文章来源地址https://www.toymoban.com/news/detail-457079.html

查问题,栈回溯,可以看到进入了ap3216c_open,然后在i2c_transfer中出问题了

#ifndef AP3216C_H

#define AP3216C_H

/***************************************************************

Copyright © ALIENTEK Co., Ltd. 1998-2029. All rights reserved.

文件名		: ap3216creg.h

作者	  	: 左忠凯

版本	   	: V1.0

描述	   	: AP3216C寄存器地址描述头文件

其他	   	: 无

论坛 	   	: www.openedv.com

日志	   	: 初版V1.0 2019/9/2 左忠凯创建

***************************************************************/



// 这个头文件用来存放ap3216c的寄存器信息

#define AP3216C_ADDR    	0X1E	/* AP3216C器件地址  */



/* AP3316C寄存器 */

#define AP3216C_SYSTEMCONG	0x00	/* 配置寄存器       */

#define AP3216C_INTSTATUS	0X01	/* 中断状态寄存器   */

#define AP3216C_INTCLEAR	0X02	/* 中断清除寄存器   */

#define AP3216C_IRDATALOW	0x0A	/* IR数据低字节     */

#define AP3216C_IRDATAHIGH	0x0B	/* IR数据高字节     */

#define AP3216C_ALSDATALOW	0x0C	/* ALS数据低字节    */

#define AP3216C_ALSDATAHIGH	0X0D	/* ALS数据高字节    */

#define AP3216C_PSDATALOW	0X0E	/* PS数据低字节     */

#define AP3216C_PSDATAHIGH	0X0F	/* PS数据高字节     */



#endif



#include <linux/types.h>

#include <linux/kernel.h>

#include <linux/delay.h>

#include <linux/ide.h>

#include <linux/init.h>

#include <linux/module.h>

#include <linux/errno.h>

#include <linux/gpio.h>

#include <linux/cdev.h>

#include <linux/device.h>

#include <linux/of_gpio.h>

#include <linux/semaphore.h>

#include <linux/timer.h>

#include <linux/i2c.h>

#include <asm/mach/map.h>

#include <asm/uaccess.h>

#include <asm/io.h>

#include "ap3216creg.h"

/***************************************************************

Copyright © ALIENTEK Co., Ltd. 1998-2029. All rights reserved.

文件名		: ap3216c.c

作者	  	: 左忠凯

版本	   	: V1.0

描述	   	: AP3216C驱动程序

其他	   	: 无

论坛 	   	: www.openedv.com

日志	   	: 初版V1.0 2019/9/2 左忠凯创建

***************************************************************/

#define AP3216C_CNT	1

#define AP3216C_NAME	"ap3216c"



struct ap3216c_dev {

	dev_t devid;			/* 设备号 	 */

	struct cdev cdev;		/* cdev 	*/

	struct class *class;	/* 类 		*/

	struct device *device;	/* 设备 	 */

	struct device_node	*nd; /* 设备节点 */

	int major;			/* 主设备号 */

	void *private_data;	/* 私有数据 */

	unsigned short ir, als, ps;		/* 三个光传感器数据 注意这里数据类型short*/

};



static struct ap3216c_dev ap3216cdev;



/*

 * @description	: 从ap3216c读取多个寄存器数据

 * @param - dev:  ap3216c设备

 * @param - reg:  要读取的寄存器首地址

 * @param - val:  读取到的数据

 * @param - len:  要读取的数据长度

 * @return 		: 操作结果

 * 

 * 读取AP3216C的N个寄存器值

 */

//读 read 写 write函数是IIC的难点和重点//

static int ap3216c_read_regs(struct ap3216c_dev *dev, u8 reg, void *val, int len)

{

	int ret;

	struct i2c_msg msg[2];

	// 从私有变量中获取一系列数据

	struct i2c_client *client = (struct i2c_client *)dev->private_data;



	/* msg[0]为发送要读取的首地址 */

	msg[0].addr = client->addr;			/* 从机地址 也就是ap3216c地址 */

	msg[0].flags = 0;								/* 标记为要发送的数据 */

	msg[0].buf = &reg;							/* 要发送的数据马也就是寄存器地址 */

	msg[0].len = 1;									/* 要发送的寄存器地址长度为1*/



	/* msg[1]读取数据 */

	msg[1].addr = client->addr;			/* 从机地址 也就是ap3216c地址 */

	msg[1].flags = I2C_M_RD;				/* 标记为读数据*/

	msg[1].buf = val;								/* 读取数据缓冲区,接收到的从机发送的数据 */

	msg[1].len = len;								/* 要读取的寄存器长度*/



  // i2c_transfer 即能向寄存器里面写数据,也能向寄存器里面读 

	//  从哪读要告诉他

	ret = i2c_transfer(client->adapter, msg, 2);

	if(ret == 2) {

		ret = 0;

	} else {

		printk("i2c rd failed=%d reg=%06x len=%d\n",ret, reg, len);

		ret = -EREMOTEIO;

	}

	return ret;

}



/*

 * @description	: 向ap3216c多个寄存器写入数据

 * @param - dev:  ap3216c设备

 * @param - reg:  要写入的寄存器首地址

 * @param - val:  要写入的数据缓冲区

 * @param - len:  要写入的数据长度

 * @return 	  :   操作结果

 * 

 * 向AP3216C写N个寄存器的数据

 */

static s32 ap3216c_write_regs(struct ap3216c_dev *dev, u8 reg, u8 *buf, u8 len)

{

	u8 b[256];

	struct i2c_msg msg;

	struct i2c_client *client = (struct i2c_client *)dev->private_data;

	

	b[0] = reg;					/* 寄存器首地址 */

	memcpy(&b[1],buf,len);		/* 将要写入的数据 拷贝 到数组b里面 */

		

	msg.addr = client->addr;	/* 从机地址 也就是ap3216c地址 */

	msg.flags = 0;				/* 表示为要发送的数据 */

	msg.buf = b;				/* 要发送的数据,寄存器地址 + 实际数据 */

	msg.len = len + 1;			/* 要写入的数据长度 寄存器地址长度 + 实际的数据长度*/



	return i2c_transfer(client->adapter, &msg, 1);

}



/*

 * @description	: 读取ap3216c指定寄存器值,读取一个寄存器

 * @param - dev:  ap3216c设备

 * @param - reg:  要读取的寄存器

 * @return 	  :   读取到的寄存器值

 * 

 * 读取AP3216C的 1 个寄存器值

 */

static unsigned char ap3216c_read_reg(struct ap3216c_dev *dev, u8 reg)

{

	u8 data = 0;



	// 直接调用写的函数,长度为 1

	ap3216c_read_regs(dev, reg, &data, 1);

	return data;



#if 0

	struct i2c_client *client = (struct i2c_client *)dev->private_data;

	return i2c_smbus_read_byte_data(client, reg);

#endif

}



/*

 * @description	: 向ap3216c指定寄存器写入指定的值,写一个寄存器

 * @param - dev:  ap3216c设备

 * @param - reg:  要写的寄存器

 * @param - data: 要写入的值

 * @return   :    无

 */

static void ap3216c_write_reg(struct ap3216c_dev *dev, u8 reg, u8 data)

{

	u8 buf = 0;

	buf = data;

	ap3216c_write_regs(dev, reg, &buf, 1);

}



/*

 * @description	: 读取AP3216C的数据,读取原始数据,包括ALS,PS和IR, 注意!

 *				: 如果同时打开ALS,IR+PS的话两次数据读取的时间间隔要大于112.5ms

 * @param - ir	: ir数据

 * @param - ps 	: ps数据

 * @param - ps 	: als数据 

 * @return 		: 无。

 */

void ap3216c_readdata(struct ap3216c_dev *dev)

{

	unsigned char i =0;

    unsigned char buf[6];

	

	/* 循环读取所有传感器数据 */

    for(i = 0; i < 6; i++)	

    {

        buf[i] = ap3216c_read_reg(dev, AP3216C_IRDATALOW + i);	

    }



    if(buf[0] & 0X80) 	/* IR_OF位为1,则数据无效 */

		dev->ir = 0;

		//从buf[0] 数组里面取 位 

	else 				/* 读取IR传感器的数据   		*/

		dev->ir = ((unsigned short)buf[1] << 2) | (buf[0] & 0X03); 			

	

		dev->als = ((unsigned short)buf[3] << 8) | buf[2];	/* 读取ALS传感器的数据 			 */  

	

    if(buf[4] & 0x40)	/* IR_OF位为1,则数据无效 			*/

		dev->ps = 0;    													

	else 				/* 读取PS传感器的数据    */

		dev->ps = ((unsigned short)(buf[5] & 0X3F) << 4) | (buf[4] & 0X0F); 

}



//

///之下是IIC驱动框架搭建,之上时AP3216C寄存器数据读写函数编写/

//

/*

 * @description		: 打开设备

 * @param - inode 	: 传递给驱动的inode

 * @param - filp 	: 设备文件,file结构体有个叫做private_data的成员变量

 * 					  一般在open的时候将private_data指向设备结构体。

 * @return 			: 0 成功;其他 失败

 */

static int ap3216c_open(struct inode *inode, struct file *filp)

{

	filp->private_data = &ap3216cdev;



	/* 初始化AP3216C */

	ap3216c_write_reg(&ap3216cdev, AP3216C_SYSTEMCONG, 0x04);		/* 复位AP3216C 			*/

	mdelay(50);														/* AP3216C复位最少10ms 	*/

	ap3216c_write_reg(&ap3216cdev, AP3216C_SYSTEMCONG, 0X03);		/* 开启ALS、PS+IR 		*/

	return 0;

}



/*

 * @description		: 从设备读取数据 

 * @param - filp 	: 要打开的设备文件(文件描述符)

 * @param - buf 	: 返回给用户空间的数据缓冲区

 * @param - cnt 	: 要读取的数据长度

 * @param - offt 	: 相对于文件首地址的偏移

 * @return 			: 读取的字节数,如果为负值,表示读取失败

 */

static ssize_t ap3216c_read(struct file *filp, char __user *buf, size_t cnt, loff_t *off)

{

	short data[3];

	long err = 0;



	struct ap3216c_dev *dev = (struct ap3216c_dev *)filp->private_data;

	/* 向应用返回AP3216C的原始数据  往里填数据*/

	ap3216c_readdata(dev);



	data[0] = dev->ir;

	data[1] = dev->als;

	data[2] = dev->ps;

	err = copy_to_user(buf, data, sizeof(data));

	return 0;

}



/*

 * @description		: 关闭/释放设备

 * @param - filp 	: 要关闭的设备文件(文件描述符)

 * @return 			: 0 成功;其他 失败

 */

static int ap3216c_release(struct inode *inode, struct file *filp)

{

	return 0;

}



/* AP3216C操作函数 */

static const struct file_operations ap3216c_ops = {

	.owner = THIS_MODULE,

	.open = ap3216c_open,

	.read = ap3216c_read,

	.release = ap3216c_release,

};



 /*

  * @description     : i2c驱动的probe函数,当驱动与

  *                    设备匹配以后此函数就会执行

  * @param - client  : i2c设备

  * @param - id      : i2c设备ID

  * @return          : 0,成功;其他负值,失败

	*  

								probe函数

  */

static int ap3216c_probe(struct i2c_client *client, const struct i2c_device_id *id)

{

	/* 搭建一套完整的字符设备驱动  */

	/* 1、构建设备号 */

	if (ap3216cdev.major) {

		ap3216cdev.devid = MKDEV(ap3216cdev.major, 0);

		register_chrdev_region(ap3216cdev.devid, AP3216C_CNT, AP3216C_NAME);

	} else {

		alloc_chrdev_region(&ap3216cdev.devid, 0, AP3216C_CNT, AP3216C_NAME);

		ap3216cdev.major = MAJOR(ap3216cdev.devid);

	}



	/* 2、注册设备 */

	cdev_init(&ap3216cdev.cdev, &ap3216c_ops);

	cdev_add(&ap3216cdev.cdev, ap3216cdev.devid, AP3216C_CNT);



	/* 3、创建类 */

	ap3216cdev.class = class_create(THIS_MODULE, AP3216C_NAME);

	if (IS_ERR(ap3216cdev.class)) {

		return PTR_ERR(ap3216cdev.class);

	}



	/* 4、创建设备 */

	ap3216cdev.device = device_create(ap3216cdev.class, NULL, ap3216cdev.devid, NULL, AP3216C_NAME);

	if (IS_ERR(ap3216cdev.device)) {

		return PTR_ERR(ap3216cdev.device);

	}



	// 让私有数据ap3216cdev 获得client

	ap3216cdev.private_data = client;



	return 0;

}



/*

 * @description     : i2c驱动的remove函数,移除i2c驱动的时候此函数会执行

 * @param - client 	: i2c设备

 * @return          : 0,成功;其他负值,失败

 */

static int ap3216c_remove(struct i2c_client *client)

{

	/* 删除设备 */

	cdev_del(&ap3216cdev.cdev);

	unregister_chrdev_region(ap3216cdev.devid, AP3216C_CNT);



	/* 注销掉类和设备 */

	device_destroy(ap3216cdev.class, ap3216cdev.devid);

	class_destroy(ap3216cdev.class);

	return 0;

}





/* 两种匹配表 probe函数调用时候判断两个都成立才行,后面的不为空就行 */

/* 传统匹配方式ID列表 */

static const struct i2c_device_id ap3216c_id[] = {

	{"alientek,ap3216c", 0},  

	{}

};



/* 设备树匹配列表 */

static const struct of_device_id ap3216c_of_match[] = {

	{ .compatible = "alientek,ap3216c" },

	{ /* Sentinel */ }

};



/* i2c驱动结构体

		定义一个probe函数,一个remove函数

 */	

/********************I2C驱动的重中之重*************************/

static struct i2c_driver ap3216c_driver = {

	.probe = ap3216c_probe,

	.remove = ap3216c_remove,

	.driver = {

			.owner = THIS_MODULE,

		   	.name = "ap3216c",

		   	.of_match_table = ap3216c_of_match, 

		   },

	.id_table = ap3216c_id,

};

		   

/*

 * @description	: 驱动入口函数

 * @param 		: 无

 * @return 		: 无

 */

static int __init ap3216c_init(void)

{

	int ret = 0;



	//注册i2c_driver

	ret = i2c_add_driver(&ap3216c_driver);

	return ret;

}



/*

 * @description	: 驱动出口函数

 * @param 		: 无

 * @return 		: 无

 */

static void __exit ap3216c_exit(void)

{

	//注销i2c_driver

	i2c_del_driver(&ap3216c_driver);

}



/* module_i2c_driver(ap3216c_driver) */



module_init(ap3216c_init);

module_exit(ap3216c_exit);

MODULE_LICENSE("GPL");

MODULE_AUTHOR("zuozhongkai");







#include "stdio.h"

#include "unistd.h"

#include "sys/types.h"

#include "sys/stat.h"

#include "sys/ioctl.h"

#include "fcntl.h"

#include "stdlib.h"

#include "string.h"

#include <poll.h>

#include <sys/select.h>

#include <sys/time.h>

#include <signal.h>

#include <fcntl.h>

/***************************************************************

Copyright © ALIENTEK Co., Ltd. 1998-2029. All rights reserved.

文件名		: ap3216cApp.c

作者	  	: 左忠凯

版本	   	: V1.0

描述	   	: ap3216c设备测试APP。

其他	   	: 无

使用方法	 :./ap3216cApp /dev/ap3216c

论坛 	   	: www.openedv.com

日志	   	: 初版V1.0 2019/9/20 左忠凯创建

***************************************************************/



/*

 * @description		: main主程序

 * @param - argc 	: argv数组元素个数

 * @param - argv 	: 具体参数

 * @return 			: 0 成功;其他 失败

 */

int main(int argc, char *argv[])

{

	int fd;

	char *filename;

	unsigned short databuf[3];

	unsigned short ir, als, ps;

	int ret = 0;



	if (argc != 2) {

		printf("Error Usage!\r\n");

		return -1;

	}



	filename = argv[1];

	fd = open(filename, O_RDWR);

	if(fd < 0) {

		printf("can't open file %s\r\n", filename);

		return -1;

	}



	

	// 老规矩,这上面的部分都是模板,下面才是这个app真正写的

	// 循环读取

	while (1) {

		// 读取数据

		ret = read(fd, databuf, sizeof(databuf));

		// 判断数据 光强度(ALS)、接近距离(PS)和红外线强度(IR)

		if(ret == 0) { 			/* 数据读取成功 */

			ir =  databuf[0]; 	/* ir传感器数据 */

			als = databuf[1]; 	/* als传感器数据 */

			ps =  databuf[2]; 	/* ps传感器数据 */

			printf("ir = %d, als = %d, ps = %d\r\n", ir, als, ps);

		}

		usleep(200000); /*100ms */

	}

	close(fd);	/* 关闭文件 */	

	return 0;

}



到了这里,关于Linux驱动开发12 IIC驱动的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 4、Linux驱动开发:设备-设备号&设备号注册

    🍅点击这里查看所有博文   随着自己工作的进行,接触到的技术栈也越来越多。给我一个很直观的感受就是,某一项技术/经验在刚开始接触的时候都记得很清楚。往往过了几个月都会忘记的差不多了,只有经常会用到的东西才有可能真正记下来。存在很多在特殊情况下有

    2024年02月15日
    浏览(45)
  • Linux驱动开发实战(一)——设备驱动模型

    在早期的Linux内核中并没有为设备驱动提供统一的设备模型。随着内核的不断扩大及系统更加复杂,编写一个驱动程序越来越困难,所以在Linux2.6内核中添加了一个统一的设备模型。这样,写设备驱动程序就稍微容易一些了。本章将对设备模型进行详细的介绍。 设备驱动模型

    2024年02月16日
    浏览(34)
  • Linux设备驱动开发 - 虚拟时钟Clock驱动示例

    By: fulinux E-mail: fulinux@sina.com Blog: https://blog.csdn.net/fulinus 喜欢的盆友欢迎点赞和订阅! 你的喜欢就是我写作的动力! 很多设备里面系统时钟架构极其复杂,让学习Clock驱动的盆友头大。这里我参考S3C2440的clock驱动写了一个virtual clock,即虚拟时钟驱动,分别包含clock的provider和

    2023年04月21日
    浏览(30)
  • Linux -- 字符设备驱动--LED的驱动开发(初级框架)

    看原理图确定引脚,确定引脚输出什么电平才能点亮 / 熄灭 LED 看主芯片手册,确定寄存器操作方法:哪些寄存器?哪些位?地址是? 编写驱动:先写框架,再写硬件操作的代码 注意 :在芯片手册中确定的寄存器地址被称为 物理地址 ,在 Linux 内核中无法直接使用。 需要使

    2024年04月28日
    浏览(22)
  • 嵌入式Linux驱动开发 04:基于设备树的驱动开发

    前面文章 《嵌入式Linux驱动开发 03:平台(platform)总线驱动模型》 引入了资源和驱动分离的概念,这篇文章将在前面基础上更进一步,引入设备树的概念。 在平台总线驱动模型中资源和驱动已经从逻辑上和代码组织上进行了分离,但每次调整资源还是会涉及到内核,所以现

    2024年02月16日
    浏览(55)
  • 正点原子嵌入式linux驱动开发——Linux 网络设备驱动

    网络驱动是linux里面驱动三巨头之一 ,linux下的网络功能非常强大,嵌入式linux中也常常用到网络功能。前面已经讲过了字符设备驱动和块设备驱动,本章就来学习一下linux里面的 网络设备驱动 。 本次笔记中讨论的都是有线网络! 提起网络,一般想到的硬件就是“网卡”。在

    2024年01月17日
    浏览(55)
  • 嵌入式Linux系统中的设备驱动开发:从设备树到驱动实现

    大家好,今天给大家介绍 嵌入式Linux系统中的设备驱动开发:从设备树到驱动实现 ,文章末尾附有分享大家一个资料包,差不多150多G。里面学习内容、面经、项目都比较新也比较全! 可进群免费领取。 在嵌入式Linux系统中,设备驱动是连接硬件设备和操作系统之间的桥梁。

    2024年02月19日
    浏览(46)
  • 【Linux驱动开发】设备树详解(三)设备树Kernel解析

    ​ ​ 活动地址:CSDN21天学习挑战赛 【Linux驱动开发】设备树详解(一)设备树基础介绍 【Linux驱动开发】设备树详解(二)设备树语法详解 【Linux驱动开发】设备树详解(三)设备树Kernel解析   个人主页:董哥聊技术 我是董哥,嵌入式领域新星创作者 创作理念:专注分享

    2023年04月24日
    浏览(39)
  • 深入探讨Linux驱动开发:Linux设备树

    设备树(Device Tree,简称 DT)是一种在嵌入式系统中描述硬件设备的一种数据结构和编程语言。它用于将硬件设备的配置信息以树形结构的方式进行描述,以便操作系统(如 Linux)可以根据这些信息正确地识别、配置和管理硬件设备。 设备树最初被引入到 Linux 内核中,用于解

    2023年04月27日
    浏览(36)
  • Linux设备驱动开发学习笔记(等待队列,锁,字符驱动程序,设备树,i2C...)

    container_of函数可以通过结构体的成员变量检索出整个结构体 函数原型: 内核开发者只实现了循环双链表,因为这个结构能够实现FIFO和LIFO,并且内核开发者要保持最少代码。 为了支持链表,代码中要添加的头文件是linux/list.h。内核中链表实现核心部分的数据结构 是struct li

    2024年01月22日
    浏览(38)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包