精华内容
下载资源
问答
  •  本书从最简单的点亮一个led开始,由浅入深地讲解,使读者最终可以配置、移植、裁剪内核,编写驱动程序,移植gui系统,掌握整个嵌入式linux系统的开发方法。 本书由浅入深,循序渐进,适合刚接触嵌入式linux的初学...
  • 嵌入式Linux开发教程(上册)》由浅入深、结构合理、图文并茂,可操作性强,读者可跟着一步步进行操作和学习,非常适合嵌入式Linux开发初级工程师及准备往嵌入式Linux方向发展的电子工程师和单片机工程师使用,也可...

    url_none.png

    《嵌入式Linux开发教程(上册)》由浅入深、结构合理、图文并茂,可操作性强,读者可跟着一步步进行操作和学习,非常适合嵌入式Linux开发初级工程师及准备往嵌入式Linux方向发展的电子工程师和单片机工程师使用,也可作为高校非计算机专业高年级学生学习嵌入式Linux的参考教材。

    ¥71.10定价:¥79.97(8.9折)

    /2016-03-01

    url_none.png

    《嵌入式Linux开发教程(上册)》由浅入深、结构合理、图文并茂,可操作性强,读者可跟着一步步进行操作和学习,非常适合嵌入式Linux开发初级工程师及准备往嵌入式Linux方向发展的电子工程师和单片机工程师使用,也可作为高校非计算机专业高年级学生学习嵌入式Linux的参考教材。

    ¥71.10定价:¥554.34(1.29折)

    /2016-03-01

    url_none.png

    《嵌入式Linux开发教程(上册)》由浅入深、结构合理、图文并茂,可操作性强,读者可跟着一步步进行操作和学习,非常适合嵌入式Linux开发初级工程师及准备往嵌入式Linux方向发展的电子工程师和单片机工程师使用,也可作为高校非计算机专业高年级学生学习嵌入式Linux的参考教材。

    ¥50.40定价:¥249.50(2.03折)

    /2016-03-01

    url_none.png

    本教程是面向嵌入式Linux学习和产品开发的入门教程,分上下两册。本书为下册,分3篇,共16章,内容涵盖Linux内核、设备驱动、系统整合和产品化等方面。其中,设备驱动部分是重点内容,从模块编写开始,深入浅出地讲述了驱动编写的各个方面;之后以Linux子系统为单位,分别讲述了各常用外设的驱动编写或移植,如LED、GPIO、I2C、SPI、Input子系统、串口、声卡、WiFi网卡和3G模块等。本书由浅入深,结构合理,实用性强,提供的范例稍加改动即可应用于实际项目。阅读本书需要有一定Linux使用基础,适合于准备往驱动方向发展的嵌入式Linux开发工程师,也可作为高校非计算机专业高年级学生学习嵌入式Linux驱动的参考教材。

    ¥33.06定价:¥187.32(1.77折)

    url_none.png

    《嵌入式Linux开发教程(上册)》由浅入深、结构合理、图文并茂,可操作性强,读者可跟着一步步进行操作和学习,非常适合嵌入式Linux开发初级工程师及准备往嵌入式Linux方向发展的电子工程师和单片机工程师使用,也可作为高校非计算机专业高年级学生学习嵌入式Linux的参考教材。

    ¥77.00定价:¥79.00(9.75折)

    /2016-03-01

    url_none.png

    《嵌入式Linux开发教程(上册)》由浅入深、结构合理、图文并茂,可操作性强,读者可跟着一步步进行操作和学习,非常适合嵌入式Linux开发初级工程师及准备往嵌入式Linux方向发展的电子工程师和单片机工程师使用,也可作为高校非计算机专业高年级学生学习嵌入式Linux的参考教材。

    ¥73.00定价:¥191.75(3.81折)

    url_none.png

    本教程是面向嵌入式Linux学习和产品开发的入门教程,分上下两册。本书为下册,分3篇,共16章,内容涵盖Linux内核、设备驱动、系统整合和产品化等方面。其中,设备驱动部分是重点内容,从模块编写开始,深入浅出地讲述了驱动编写的各个方面;之后以Linux子系统为单位,分别讲述了各常用外设的驱动编写或移植,如LED、GPIO、I2C、SPI、Input子系统、串口、声卡、WiFi网卡和3G模块等。本书由浅入深,结构合理,实用性强,提供的范例稍加改动即可应用于实际项目。阅读本书需要有一定Linux使用基础,适合于准备往驱动方向发展的嵌入式Linux开发工程师,也可作为高校非计算机专业高年级学生学习嵌入式Linux驱动的参考教材。

    ¥33.61定价:¥232.33(1.45折)

    url_none.png

    《嵌入式Linux开发教程(上册)》由浅入深、结构合理、图文并茂,可操作性强,读者可跟着一步步进行操作和学习,非常适合嵌入式Linux开发初级工程师及准备往嵌入式Linux方向发展的电子工程师和单片机工程师使用,也可作为高校非计算机专业高年级学生学习嵌入式Linux的参考教材。

    ¥78.27定价:¥278.27(2.82折)

    url_none.png

    《嵌入式Linux开发教程(上册)》由浅入深、结构合理、图文并茂,可操作性强,读者可跟着一步步进行操作和学习,非常适合嵌入式Linux开发初级工程师及准备往嵌入式Linux方向发展的电子工程师和单片机工程师使用,也可作为高校非计算机专业高年级学生学习嵌入式Linux的参考教材。

    ¥56.10定价:¥79.00(7.11折)

    url_none.png

    本教程是面向嵌入式Linux学习和产品开发的入门教程,分上下两册。本书为下册,分3篇,共16章,内容涵盖Linux内核、设备驱动、系统整合和产品化等方面。其中,设备驱动部分是重点内容,从模块编写开始,深入浅出地讲述了驱动编写的各个方面;之后以Linux子系统为单位,分别讲述了各常用外设的驱动编写或移植,如LED、GPIO、I2C、SPI、Input子系统、串口、声卡、WiFi网卡和3G模块等。本书由浅入深,结构合理,实用性强,提供的范例稍加改动即可应用于实际项目。阅读本书需要有一定Linux使用基础,适合于准备往驱动方向发展的嵌入式Linux开发工程师,也可作为高校非计算机专业高年级学生学习嵌入式Linux驱动的参考教材。

    ¥164.82定价:¥811.83(2.04折)

    url_none.png

    《嵌入式Linux开发教程(上册)》由浅入深、结构合理、图文并茂,可操作性强,读者可跟着一步步进行操作和学习,非常适合嵌入式Linux开发初级工程师及准备往嵌入式Linux方向发展的电子工程师和单片机工程师使用,也可作为高校非计算机专业高年级学生学习嵌入式Linux的参考教材。

    ¥55.00定价:¥186.00(2.96折)

    url_none.png

    《嵌入式Linux开发教程(上册)》由浅入深、结构合理、图文并茂,可操作性强,读者可跟着一步步进行操作和学习,非常适合嵌入式Linux开发初级工程师及准备往嵌入式Linux方向发展的电子工程师和单片机工程师使用,也可作为高校非计算机专业高年级学生学习嵌入式Linux的参考教材。

    ¥63.52定价:¥80.06(7.94折)

    /2016-03-01

    展开全文
  • 嵌入式Linux应用程序开发嵌入式Linux内核驱动进阶班 .嵌入式Linux内核驱动深入班 .嵌入式ARM系统精讲班 嵌入式Linux高级项目班 1.嵌入式体验入门班 (移动图像监控系统) (嵌入式MP3播放器)(H.264远程视频监控) ...
  • 嵌入式Linux应用开发完全手册.pdf

    热门讨论 2013-02-17 16:18:23
     第1篇 嵌入式Linux开发环境构建篇 第1章 嵌入式Linux开发概述 第2章 嵌入式Linux开发环境构建 第3章 嵌入式编程基础知识 第4章 Windows、Linux环境下相关工具、命令的使用  第2篇 ARM9嵌入式系统基础...
  •   在嵌入式Linux开发18——字符设备驱动开发2中,我们直接在驱动文件 newchrled.c 中定义有关寄存器物理地址,然后使用 io_remap 函数进行内存映射,得到对应的虚拟地址,最后操作寄存器对应的虚拟地址完成对 GPIO...


    设备树LED驱动原理

      在嵌入式Linux开发18——字符设备驱动开发2中,我们直接在驱动文件 newchrled.c 中定义有关寄存器物理地址,然后使用 io_remap 函数进行内存映射,得到对应的虚拟地址,最后操作寄存器对应的虚拟地址完成对 GPIO 的初始化。本章我们在第四十二章实验基础上完成,接下来我们使用设备树来向 Linux 内核传递相关的寄存器物理地址, Linux 驱动文件使用上一章讲解的 OF函数从设备树中获取所需的属性值,然后使用获取到的属性值来初始化相关的 IO。本实验还是比较简单的,本实验重点内容如下:
    ①、在 imx6ull- emmc.dts 文件中创建相应的设备节点。
    ②、编写驱动程序,获取设备树中的相关属性值。
    ③、使用获取到的有关属性值来初始化 LED 所使用的 GPIO。

    程序编写

    1.修改设备树文件

      在根节点“/”下创建一个名为“alphaled”的子节点,打开 imx6ull-emmc.dts 文件,在根节点“/”最后面输入如下所示内容:

    led {
    #address-cells = <1>;
    #size-cells = <1>;
    compatible = "jiajia2020-led";
    status = "okay";
    reg = < 0X020C406C 0X04 /* CCM_CCGR1_BASE */
    0X020E0068 0X04 /* SW_MUX_GPIO1_IO03_BASE */
    0X020E02F4 0X04 /* SW_PAD_GPIO1_IO03_BASE */
    0X0209C000 0X04 /* GPIO1_DR_BASE */
    0X0209C004 0X04 >; /* GPIO1_GDIR_BASE */
     };
    
    

      属性#address-cells 和#size-cells 都为 1,表示 reg 属性中起始地址占用一个字长(cell),地址长度也占用一个字长(cell)。属性 compatbile 设置 led 节点兼容性为“jiajia2020-led”。属性 status 设置状态为“okay”。reg 属性,非常重要! reg 属性设置了驱动里面所要使用的寄存器物理地址,比如“0X020C406C 0X04”表示 I.MX6ULL 的 CCM_CCGR1 寄存器,其中寄存器首地址为 0X020C406C,长度为 4 个字节。
      设备树修改完成后重新编译一下即可。

    2.LED驱动程序编写

    #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.h>
    #include <linux/of_address.h>
    #include <asm/mach/map.h>
    #include <asm/uaccess.h>
    #include <asm/io.h>
    
    
    #define DTSLED_CNT			1		  	/* 设备号个数 */
    #define DTSLED_NAME			"jiajia2020-led"	/* 名字 */
    #define LEDOFF 					0			/* 关灯 */
    #define LEDON 					1			/* 开灯 */
    
    /* 映射后的寄存器虚拟地址指针 */
    static void __iomem *IMX6U_CCM_CCGR1;
    static void __iomem *SW_MUX_GPIO1_IO03;
    static void __iomem *SW_PAD_GPIO1_IO03;
    static void __iomem *GPIO1_DR;
    static void __iomem *GPIO1_GDIR;
    
    /* dtsled设备结构体 */
    struct dtsled_dev{
    	dev_t devid;			/* 设备号 	 */
    	struct cdev cdev;		/* cdev 	*/
    	struct class *class;		/* 类 		*/
    	struct device *device;	/* 设备 	 */
    	int major;				/* 主设备号	  */
    	int minor;				/* 次设备号   */
    	struct device_node	*nd; /* 设备节点 */
    };
    
    struct dtsled_dev dtsled;	/* led设备 */
    
    /*
     * @description		: LED打开/关闭
     * @param - sta 	: LEDON(0) 打开LED,LEDOFF(1) 关闭LED
     * @return 			: 无
     */
    void led_switch(u8 sta)
    {
    	u32 val = 0;
    	if(sta == LEDON) {
    		val = readl(GPIO1_DR);
    		val &= ~(1 << 3);	
    		writel(val, GPIO1_DR);
    	}else if(sta == LEDOFF) {
    		val = readl(GPIO1_DR);
    		val|= (1 << 3);	
    		writel(val, GPIO1_DR);
    	}	
    }
    
    /*
     * @description		: 打开设备
     * @param - inode 	: 传递给驱动的inode
     * @param - filp 	: 设备文件,file结构体有个叫做private_data的成员变量
     * 					  一般在open的时候将private_data指向设备结构体。
     * @return 			: 0 成功;其他 失败
     */
    static int led_open(struct inode *inode, struct file *filp)
    {
    	filp->private_data = &dtsled; /* 设置私有数据 */
    	return 0;
    }
    
    /*
     * @description		: 从设备读取数据 
     * @param - filp 	: 要打开的设备文件(文件描述符)
     * @param - buf 	: 返回给用户空间的数据缓冲区
     * @param - cnt 	: 要读取的数据长度
     * @param - offt 	: 相对于文件首地址的偏移
     * @return 			: 读取的字节数,如果为负值,表示读取失败
     */
    static ssize_t led_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt)
    {
    	return 0;
    }
    
    /*
     * @description		: 向设备写数据 
     * @param - filp 	: 设备文件,表示打开的文件描述符
     * @param - buf 	: 要写给设备写入的数据
     * @param - cnt 	: 要写入的数据长度
     * @param - offt 	: 相对于文件首地址的偏移
     * @return 			: 写入的字节数,如果为负值,表示写入失败
     */
    static ssize_t led_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)
    {
    	int retvalue;
    	unsigned char databuf[1];
    	unsigned char ledstat;
    
    	retvalue = copy_from_user(databuf, buf, cnt);
    	if(retvalue < 0) {
    		printk("kernel write failed!\r\n");
    		return -EFAULT;
    	}
    
    	ledstat = databuf[0];		/* 获取状态值 */
    
    	if(ledstat == LEDON) {	
    		led_switch(LEDON);		/* 打开LED灯 */
    	} else if(ledstat == LEDOFF) {
    		led_switch(LEDOFF);	/* 关闭LED灯 */
    	}
    	return 0;
    }
    
    /*
     * @description		: 关闭/释放设备
     * @param - filp 	: 要关闭的设备文件(文件描述符)
     * @return 			: 0 成功;其他 失败
     */
    static int led_release(struct inode *inode, struct file *filp)
    {
    	return 0;
    }
    
    /* 设备操作函数 */
    static struct file_operations dtsled_fops = {
    	.owner = THIS_MODULE,
    	.open = led_open,
    	.read = led_read,
    	.write = led_write,
    	.release = 	led_release,
    };
    
    /*
     * @description	: 驱动出口函数
     * @param 		: 无
     * @return 		: 无
     */
    static int __init led_init(void)
    {
    	u32 val = 0;
    	int ret;
    	u32 regdata[14];
    	const char *str;
    	struct property *proper;
    
    	/* 获取设备树中的属性数据 */
    	/* 1、获取设备节点:jiajia2020-led */
    	dtsled.nd = of_find_node_by_path("/jiajia2020-led");
    	if(dtsled.nd == NULL) {
    		printk("led node nost find!\r\n");
    		return -EINVAL;
    	} else {
    		printk("led node find!\r\n");
    	}
    
    	/* 2、获取compatible属性内容 */
    	proper = of_find_property(dtsled.nd, "compatible", NULL);
    	if(proper == NULL) {
    		printk("compatible property find failed\r\n");
    	} else {
    		printk("compatible = %s\r\n", (char*)proper->value);
    	}
    
    	/* 3、获取status属性内容 */
    	ret = of_property_read_string(dtsled.nd, "status", &str);
    	if(ret < 0){
    		printk("status read failed!\r\n");
    	} else {
    		printk("status = %s\r\n",str);
    	}
    
    	/* 4、获取reg属性内容 */
    	ret = of_property_read_u32_array(dtsled.nd, "reg", regdata, 10);
    	if(ret < 0) {
    		printk("reg property read failed!\r\n");
    	} else {
    		u8 i = 0;
    		printk("reg data:\r\n");
    		for(i = 0; i < 10; i++)
    			printk("%#X ", regdata[i]);
    		printk("\r\n");
    	}
    
    	/* 初始化LED */
    #if 0
    	/* 1、寄存器地址映射 */
    	IMX6U_CCM_CCGR1 = ioremap(regdata[0], regdata[1]);
    	SW_MUX_GPIO1_IO03 = ioremap(regdata[2], regdata[3]);
      	SW_PAD_GPIO1_IO03 = ioremap(regdata[4], regdata[5]);
    	GPIO1_DR = ioremap(regdata[6], regdata[7]);
    	GPIO1_GDIR = ioremap(regdata[8], regdata[9]);
    #else
    	IMX6U_CCM_CCGR1 = of_iomap(dtsled.nd, 0);
    	SW_MUX_GPIO1_IO03 = of_iomap(dtsled.nd, 1);
      	SW_PAD_GPIO1_IO03 = of_iomap(dtsled.nd, 2);
    	GPIO1_DR = of_iomap(dtsled.nd, 3);
    	GPIO1_GDIR = of_iomap(dtsled.nd, 4);
    #endif
    
    	/* 2、使能GPIO1时钟 */
    	val = readl(IMX6U_CCM_CCGR1);
    	val &= ~(3 << 26);	/* 清楚以前的设置 */
    	val |= (3 << 26);	/* 设置新值 */
    	writel(val, IMX6U_CCM_CCGR1);
    
    	/* 3、设置GPIO1_IO03的复用功能,将其复用为
    	 *    GPIO1_IO03,最后设置IO属性。
    	 */
    	writel(5, SW_MUX_GPIO1_IO03);
    	
    	/*寄存器SW_PAD_GPIO1_IO03设置IO属性
    	 *bit 16:0 HYS关闭
    	 *bit [15:14]: 00 默认下拉
         *bit [13]: 0 kepper功能
         *bit [12]: 1 pull/keeper使能
         *bit [11]: 0 关闭开路输出
         *bit [7:6]: 10 速度100Mhz
         *bit [5:3]: 110 R0/6驱动能力
         *bit [0]: 0 低转换率
    	 */
    	writel(0x10B0, SW_PAD_GPIO1_IO03);
    
    	/* 4、设置GPIO1_IO03为输出功能 */
    	val = readl(GPIO1_GDIR);
    	val &= ~(1 << 3);	/* 清除以前的设置 */
    	val |= (1 << 3);	/* 设置为输出 */
    	writel(val, GPIO1_GDIR);
    
    	/* 5、默认关闭LED */
    	val = readl(GPIO1_DR);
    	val |= (1 << 3);	
    	writel(val, GPIO1_DR);
    
    	/* 注册字符设备驱动 */
    	/* 1、创建设备号 */
    	if (dtsled.major) {		/*  定义了设备号 */
    		dtsled.devid = MKDEV(dtsled.major, 0);
    		register_chrdev_region(dtsled.devid, DTSLED_CNT, DTSLED_NAME);
    	} else {						/* 没有定义设备号 */
    		alloc_chrdev_region(&dtsled.devid, 0, DTSLED_CNT, DTSLED_NAME);	/* 申请设备号 */
    		dtsled.major = MAJOR(dtsled.devid);	/* 获取分配号的主设备号 */
    		dtsled.minor = MINOR(dtsled.devid);	/* 获取分配号的次设备号 */
    	}
    	printk("dtsled major=%d,minor=%d\r\n",dtsled.major, dtsled.minor);	
    	
    	/* 2、初始化cdev */
    	dtsled.cdev.owner = THIS_MODULE;
    	cdev_init(&dtsled.cdev, &dtsled_fops);
    	
    	/* 3、添加一个cdev */
    	cdev_add(&dtsled.cdev, dtsled.devid, DTSLED_CNT);
    
    	/* 4、创建类 */
    	dtsled.class = class_create(THIS_MODULE, DTSLED_NAME);
    	if (IS_ERR(dtsled.class)) {
    		return PTR_ERR(dtsled.class);
    	}
    
    	/* 5、创建设备 */
    	dtsled.device = device_create(dtsled.class, NULL, dtsled.devid, NULL, DTSLED_NAME);
    	if (IS_ERR(dtsled.device)) {
    		return PTR_ERR(dtsled.device);
    	}
    	
    	return 0;
    }
    
    /*
     * @description	: 驱动出口函数
     * @param 		: 无
     * @return 		: 无
     */
    static void __exit led_exit(void)
    {
    	/* 取消映射 */
    	iounmap(IMX6U_CCM_CCGR1);
    	iounmap(SW_MUX_GPIO1_IO03);
    	iounmap(SW_PAD_GPIO1_IO03);
    	iounmap(GPIO1_DR);
    	iounmap(GPIO1_GDIR);
    
    	/* 注销字符设备驱动 */
    	cdev_del(&dtsled.cdev);/*  删除cdev */
    	unregister_chrdev_region(dtsled.devid, DTSLED_CNT); /* 注销设备号 */
    
    	device_destroy(dtsled.class, dtsled.devid);
    	class_destroy(dtsled.class);
    }
    
    module_init(led_init);
    module_exit(led_exit);
    MODULE_LICENSE("GPL");
    MODULE_AUTHOR("jiajia2020");
    
    
    

      在设备结构体 dtsled_dev 中添加了成员变量 nd, nd 是 device_node 结构体类型指针,表示设备节点。如果我们要读取设备树某个节点的属性值,首先要先得到这个节点,一般在设备结构体中添加 device_node 指针变量来存放这个节点。而后通过 of_find_node_by_path 函数得到 alphaled 节点,后续其他的 OF 函数要使用 device_node。接着通过of_find_property 函数获取 alphaled 节点的 compatible 属性,返回值为property 结构体类型指针变量, property 的成员变量 value 表示属性值,又通过 of_property_read_string 函数获取 alphaled 节点的 status 属性值。最后通过 of_property_read_u32_array 函数获取 alphaled 节点的 reg 属性所有值,并且将获取到的值都存放到 regdata 数组中。
      编译驱动程序,得到.ko文件并拷贝到开发板中,通过modprobe指令加载驱动即可。

    展开全文
  • 41.1 LinuxLED 灯驱动原理 Linux 下的任何外设驱动,最终都是要配置相应的硬件寄存器。所以本章的 LED 灯驱动最 终也是对 I.MX6ULL 的 IO 口进行配置,与裸机实验不同的是,在 Linux 下编写驱动要符合 Linux 的...

    41.1 Linux 下 LED 灯驱动原理

    Linux 下的任何外设驱动,最终都是要配置相应的硬件寄存器。所以本章的 LED 灯驱动最
    终也是对 I.MX6ULL 的 IO 口进行配置,与裸机实验不同的是,在 Linux 下编写驱动要符合 Linux
    的驱动框架。
    41.1.1 地址映射
    在编写驱动之前,我们需要先简单了解一下 MMU 这个神器, MMU 全称叫做 Memory
    Manage Unit,也就是内存管理单元。在老版本的 Linux 中要求处理器必须有 MMU,但是现在
    Linux 内核已经支持无 MMU 的处理器了。 MMU 主要完成的功能如下:
    ①、完成虚拟空间到物理空间的映射。
    ②、内存保护,设置存储器的访问权限,设置虚拟存储空间的缓冲特性。
    我们重点来看一下第①点,也就是虚拟空间到物理空间的映射,也叫做地址映射。首先了
    解两个地址概念:虚拟地址(VA,Virtual Address)、物理地址(PA, Physcical Address)。对于 32 位
    的处理器来说,虚拟地址范围是 2^32=4GB,我们的开发板上有 512MB 的 DDR3,这 512MB 的
    内存就是物理内存,经过 MMU 可以将其映射到整个 4GB 的虚拟空间。
    物理内存只有 512MB,虚拟内存有 4GB,那么肯定存在多个虚拟地址映射到同一个物理地
    址上去,虚拟地址范围比物理地址范围大的问题处理器自会处理,这里我们不要去深究,因为
    MMU 是很复杂的一个东西。
    Linux 内核启动的时候会初始化 MMU,设置好内存映射,设置好以后 CPU 访问的都是虚
    拟 地 址 。 比 如 I.MX6ULL 的 GPIO1_IO03 引 脚 的 复 用 寄 存 器
    IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO03 的地址为 0X020E0068。如果没有开启 MMU 的话
    直接向 0X020E0068 这个寄存器地址写入数据就可以配置 GPIO1_IO03 的复用功能。现在开启
    了 MMU,并且设置了内存映射,因此就不能直接向 0X020E0068 这个地址写入数据了。我们必
    须得到 0X020E0068 这个物理地址在 Linux 系统里面对应的虚拟地址,这里就涉及到了物理内
    存和虚拟内存之间的转换,需要用到两个函数: ioremap 和 iounmap。
    1、 ioremap 函数
    ioremap 函 数 用 于 获 取 指 定 物 理 地 址 空 间 对 应 的 虚 拟 地 址 空 间 , 定 义 在
    arch/arm/include/asm/io.h 文件中,定义如下:
    示例代码 41.1.1.1 ioremap 函数
    1 #define ioremap(cookie,size) __arm_ioremap((cookie), (size),MT_DEVICE)
    2 void __iomem * __arm_ioremap(phys_addr_t phys_addr, size_t size,unsigned int mtype)
    4 {
        5 return arch_ioremap_caller(phys_addr, size, mtype,__builtin_return_address(0));
    6 }
    ioremap 是个宏,有两个参数: cookie 和 size,真正起作用的是函数__arm_ioremap,此函
    数有三个参数和一个返回值,这些参数和返回值的含义如下:
    phys_addr:要映射给的物理起始地址。
    size:要映射的内存空间大小。
    mtype: ioremap 的类型,可以选择 MT_DEVICE、 MT_DEVICE_NONSHARED、
    MT_DEVICE_CACHED 和 MT_DEVICE_WC, ioremap 函数选择 MT_DEVICE。
    返回值: __iomem 类型的指针,指向映射后的虚拟空间首地址。
    假如我们要获取 I.MX6ULL 的 IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO03 寄存器对应
    的虚拟地址,使用如下代码即可:
    #define SW_MUX_GPIO1_IO03_BASE (0X020E0068)
    static void __iomem* SW_MUX_GPIO1_IO03;
    SW_MUX_GPIO1_IO03 = ioremap(SW_MUX_GPIO1_IO03_BASE, 4);
    宏 SW_MUX_GPIO1_IO03_BASE 是寄存器物理地址, SW_MUX_GPIO1_IO03 是映射后
    的虚拟地址。对于 I.MX6ULL 来说一个寄存器是 4 字节(32)的,因此映射的内存长度为 4。
    映射完成以后直接对 SW_MUX_GPIO1_IO03 进行读写操作即可。
    
    2、 iounmap 函数
    卸载驱动的时候需要使用 iounmap 函数释放掉 ioremap 函数所做的映射, iounmap 函数原
    型如下:
    示例代码 41.1.1.2 iounmap 函数原型
    void iounmap (volatile void __iomem *addr)
    iounmap 只有一个参数 addr,此参数就是要取消映射的虚拟地址空间首地址。假如我们现
    在要取消掉 IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO03 寄存器的地址映射,使用如下代码
    即可:
    iounmap(SW_MUX_GPIO1_IO03);
    
    41.1.2 I/O 内存访问函数
    这里说的 I/O 是输入/输出的意思,并不是我们学习单片机的时候讲的 GPIO 引脚。这里涉
    及到两个概念: I/O 端口和 I/O 内存。当外部寄存器或内存映射到 IO 空间时,称为 I/O 端口。
    当外部寄存器或内存映射到内存空间时,称为 I/O 内存。但是对于 ARM 来说没有 I/O 空间这个
    概念,因此 ARM 体系下只有 I/O 内存(可以直接理解为内存)。使用 ioremap 函数将寄存器的物
    理地址映射到虚拟地址以后,我们就可以直接通过指针访问这些地址,但是 Linux 内核不建议
    这么做,而是推荐使用一组操作函数来对映射后的内存进行读写操作。
    1、读操作函数
    读操作函数有如下几个:
    示例代码 41.1.2.1 读操作函数
    1 u8 readb(const volatile void __iomem *addr)
    2 u16 readw(const volatile void __iomem *addr)
    3 u32 readl(const volatile void __iomem *addr)
    readb、 readw 和 readl 这三个函数分别对应 8bit、 16bit 和 32bit 读操作,参数 addr 就是要
    读取写内存地址,返回值就是读取到的数据。
    2、写操作函数
    写操作函数有如下几个:
    示例代码 41.1.2.2 写操作函数
    1 void writeb(u8 value, volatile void __iomem *addr)
    2 void writew(u16 value, volatile void __iomem *addr)
    3 void writel(u32 value, volatile void __iomem *addr)
    writeb、 writew 和 writel 这三个函数分别对应 8bit、 16bit 和 32bit 写操作,参数 value 是要
    写入的数值, addr 是要写入的地址。
    

    41.3 实验程序编写

    本实验对应的例程路径为: 开发板光盘-> 2、 Linux 驱动例程-> 2_led。
    示例代码 41.3.1.1 led.c 驱动文件代码
    第 94~114 行, led_write 函数,实现对 LED 灯的开关操作,当应用程序调用 write 函数向
    led 设备写数据的时候此函数就会执行。首先通过函数 copy_from_user 获取应用程序发送过来
    的操作信息(打开还是关闭 LED),最后根据应用程序的操作信息来打开或关闭 LED 灯。
    第 140~185 行,驱动入口函数 led_init,此函数实现了 LED 的初始化工作, 147~151 行通过
    ioremap 函数获取物理寄存器地址映射后的虚拟地址,得到寄存器对应的虚拟地址以后就可以
    完成相关初始化工作了。比如是能 GPIO1 时钟、设置 GPIO1_IO03 复用功能、配置 GPIO1_IO03
    的属性等等。最后,最重要的一步!使用 register_chrdev 函数注册 led 这个字符设备。
    第 192~202 行,驱动出口函数 led_exit,首先使用函数 iounmap 取消内存映射,最后使用函
    数 unregister_chrdev 注销 led 这个字符设备。
    第 205~206 行,使用 module_init 和 module_exit 这两个函数指定 led 设备驱动加载和卸载
    函数。
    第 207~208 行,添加 LICENSE 和作者信息。
    
    41.3.2 编写测试 APP
    编写测试 APP, led 驱动加载成功以后手动创建/dev/led 节点,应用 APP 通过操作/dev/led
    文件来完成对 LED 设备的控制。向/dev/led 文件写 0 表示关闭 LED 灯,写 1 表示打开 LED 灯。
    新建 ledApp.c 文件
    

    41.4 运行测试

    41.4.1 编译驱动程序和测试 APP
    1、编译驱动程序
    编写 Makefile 文件,本章实验的 Makefile 文件和第四十章实验基本一样,只是将 obj-m 变
    量的值改为 led.o
    输入如下命令编译出驱动模块文件:
    make -j32
    编译成功以后就会生成一个名为“ led.ko”的驱动模块文件。
    2、编译测试 APP
    输入如下命令编译测试 ledApp.c 这个测试程序:
    arm-linux-gnueabihf-gcc ledApp.c -o ledApp
    
    41.4.2 运行测试
    将上一小节编译出来的 led.ko和 ledApp这两个文件拷贝到 rootfs/lib/modules/4.1.15 目录中,
    重启开发板,进入到目录 lib/modules/4.1.15 中,输入如下命令加载 led.ko 驱动模块:
    depmod //第一次加载驱动的时候需要运行此命令
    modprobe led.ko //加载驱动
    驱动加载成功以后创建“ /dev/led”设备节点,命令如下:
    mknod /dev/led c 200 0
    驱动节点创建成功以后就可以使用 ledApp 软件来测试驱动是否工作正常,输入如下命令打
    开 LED 灯:
    ./ledApp /dev/led 1 //打开 LED 灯
    在输入如下命令关闭 LED 灯:
    ./ledApp /dev/led 0 //关闭 LED 灯
    如果要卸载驱动的话输入如下命令即可:
    rmmod led.ko
    

    参考文献

    【正点原子】I.MX6U嵌入式Linux驱动开发指南V1.3.pdf

    展开全文
  • 嵌入式Linux LED GPIO

    千次阅读 2020-03-04 17:27:58
    嵌入式Linux i.MX开发板 嵌入式Linux NFS 嵌入式Linux 交叉编译工具链 Linux 中的三大类驱动:字符设备驱动、块设备驱动和网络设备驱动. GPIO属于字符设备, 驱动在买的板子上一般厂家都已改好, 当然如果扩展或者...

    前言

    这是前3篇:

    Linux 中的三大类驱动:字符设备驱动、块设备驱动和网络设备驱动. LED, GPIO属于字符设备, 驱动在买的板子上一般厂家都已改好, 当然如果扩展或者自己画新板子的话可以改设备树. 本篇介绍下GPIO, 以LED, GPIO子系统为例, 采用板子出厂配置的系统, 暂不涉及设备树的改动.

    LED

    终端

    # 查看LED
    root@mys6ull14x14:~# ls /sys/class/leds
    cpu     mmc0::  user
    # 米尔的MYS-6ULX板子上有3颗LED, 分别命名为cpu, mmc0::, user
    # user是闲置的LED, 用这个
    
    # 查看LED设备属性
    root@mys6ull14x14:~# ls /sys/class/leds/user
    brightness      max_brightness  subsystem       uevent
    device          power           trigger
    # 这里主要用brightness, 取值范围0~brightness
    # 如果不支持多级亮度, brightness取0灯灭, 非0灯亮(如1, 255)
    
    # 点亮LED, 对应米尔MYS-6ULX板子上的D12
    root@mys6ull14x14:~# echo 1 > /sys/class/leds/user/brightness
    
    # 熄灭LED
    root@mys6ull14x14:~# echo 0 > /sys/class/leds/user/brightness
    

    脚本

    此处可有脚本gpio_led.sh:

    #!/bin/bash
    echo "GPIO LED Test"
    
    LED=/sys/class/leds/user/brightness
    
    while (true)
    do
        echo 1 > $LED
        sleep 1
        echo 0 > $LED
        sleep 1
    done
    

    加权限sudo chmod 777 gpio_led.sh, 执行./gpio_led.sh, 可以看到板子上LED间隔1s闪烁.

    C语言

    直接用系统调用的方式, 新建文件main.c:

    #include <stdio.h>
    #include <unistd.h>
    #include <stdlib.h>
    #include <fcntl.h>
    
    #define USER_LED_DEV_PATH "/sys/class/leds/user/brightness"
    
    int main()
    {
        int led_fd;
        char cmd[128] = {0};
    
        printf("user led demo\r\n");
    
        led_fd = open(USER_LED_DEV_PATH, O_WRONLY);
        if (led_fd < 0)
        {
            printf("Fail to open %s device\n", USER_LED_DEV_PATH);
            exit(1);
        }
    
        while (1)
        {
            sprintf(cmd, "echo 1 > %s", USER_LED_DEV_PATH);
            system(cmd);
            sleep(1);
            sprintf(cmd, "echo 0 > %s", USER_LED_DEV_PATH);
            system(cmd);
            usleep(1000 * 300);
        }
    
        close(led_fd);
        return 0;
    }
    

    交叉编译arm-linux-gnueabihf-gcc -o led1 led1.c, 执行./led1, 可以看到板子user LED亮1s灭300ms循环…

    while中也可以改成write的方式:

        while (1)
        {
            write(led_fd, "255", 3); //3: 3个字节
            sleep(1);
            write(led_fd, "0", 1);
            usleep(1000 * 300);
        }
    

    交叉编译执行, 效果是一样的.

    GPIO子系统

    与LED子系统类似,Linux提供了GPIO子系统驱动框架,使用该驱动框架可以把CPU的GPIO引脚导出到用户空间,用户通过访问/sys文件系统进行控制,GPIO子系统支持把引脚用于基本的输入输出功能,其中输入功能还支持中断检测。

    米尔的MYS-6ULX板子上留出了两个gpio, gpio5和gpio9:
    在这里插入图片描述
    这里以37引脚的GPIO_9为例, 接上万用表:
    在这里插入图片描述

    root@mys6ull14x14:~# ls /sys/class/gpio
    export       gpiochip0    gpiochip32   gpiochip96
    gpio131      gpiochip128  gpiochip64   unexport
    
    # 向export文件写入GPIO编号可以向内核申请将该编号的GPIO导出到用户空间
    # 注意: 板子复位后设置无效
    # 反操作是unexport
    root@mys6ull14x14:~# echo 9 > /sys/class/gpio/export
    # 查看导出的gpio, 发现多了gpio9
    root@mys6ull14x14:~# ls /sys/class/gpio
    export       gpio9        gpiochip128  gpiochip64   unexport
    gpio131      gpiochip0    gpiochip32   gpiochip96
    
    # 查看gpio9属性
    root@mys6ull14x14:~# ls /sys/class/gpio/gpio9
    active_low  direction   power       uevent
    device      edge        subsystem   value
    # 使用GPIO子系统的设备则可以在用户空间灵活配置作为输入、输出或中断模式
    # 查看gpio9的方向, 下面显示是输入
    root@mys6ull14x14:~# cat /sys/class/gpio/gpio9/direction
    in
    
    # 设置gpio9为输出
    root@mys6ull14x14:~# echo out > /sys/class/gpio/gpio9/direction
    
    # gpio9输出高电平, 可用万用表检测米尔MYS-6ULX板子J2的37引脚, 应为3.3V
    root@mys6ull14x14:~# echo 1 > /sys/class/gpio/gpio9/value
    
    # gpio9输出低电平
    root@mys6ull14x14:~# echo 0 > /sys/class/gpio/gpio9/value
    
    # 取消gpio9的导出
    root@mys6ull14x14:~# echo 9 > /sys/class/gpio/unexport
    

    再回头看下米尔MYS-6ULX板子GPIO_9的定义, 实际引脚是GPIO1_IO09:
    在这里插入图片描述
    引用下 控制蜂鸣器(GPIO子系统)中的介绍:

    i.MX6ULL芯片GPIO引脚名格式通常为GPIOn_IOx,如GPIO1_IO09,其 中n是端口号,x为该组端口的引脚号,开发板采用的芯片有1-5组端口,每组端口包含的引脚从0-31不等。

    export文件使用的编号index与GPIO引脚名的转换关系:index = GPIOn_IOx = (n-1)*32 + x, 所以GPIO1_IO09对应的index是(1-1)*32+9 = 9, 直接在原理图中命名为GPIO_9.

    用C语言编写文件gpio.c:

    #include <stdio.h>
    #include <unistd.h>
    #include <stdlib.h>
    #include <fcntl.h>
    #include <string.h>
    
    #define GPIO_INDEX "9"
    
    int main()
    {
        int fd;
    
        //echo 9 > /sys/class/gpio/export
        fd = open("/sys/class/gpio/export", O_WRONLY);
        if (fd < 0)
        {
            return 1;
        }
        write(fd, GPIO_INDEX, strlen(GPIO_INDEX));
        close(fd);
    
        //echo out > /sys/class/gpio/gpio9/direction
        fd = open("/sys/class/gpio/gpio" GPIO_INDEX "/direction", O_WRONLY);
        if (fd < 0)
        {
            return 2;
        }
        write(fd, "out", strlen("out"));
        close(fd);
    
        fd = open("/sys/class/gpio/gpio" GPIO_INDEX "/value", O_WRONLY);
        if (fd < 0)
        {
            return 3;
        }
    
        while (1)
        {
            write(fd, "1", 1);
            sleep(2);
            write(fd, "0", 1);
            sleep(2);
        }
    
        close(fd);
        return 0;
    }
    

    交叉编译: arm-linux-gnueabihf-gcc -o gpio1 gpio.c, 嵌入式板子上执行./gpio1, 可以看到万用表3.3V和0V之间以2s间隔切换.

    微信公众号

    欢迎扫描关注我的微信公众号, 及时获取最新文章:
    在这里插入图片描述

    展开全文
  • LinuxLED灯驱动原理   Linux 下的任何外设驱动,最终都是要配置相应的硬件寄存器。所以本章的 LED 灯驱动最终也是对 I.MX6ULL 的 IO 口进行配置,与裸机实验不同的是,在 Linux 下编写驱动要符合 Linux的驱动...
  • if(EPIT1->SR & (1)) /* 判断比较事件发生 */ { led_switch(LED0, state); /* 定时器周期到,反转LED */ } EPIT1->SR |= 1; /* 清除中断标志位 */ }   之后我们在主函数中直接调用epit1_init...
  • 【嵌入式】基于ARM的嵌入式Linux开发总结

    万次阅读 多人点赞 2019-06-14 20:04:39
    前言嵌入式知识点复习一嵌入式知识点复习二 --体系结构嵌入式知识点复习三 --ARM-LINUX嵌入式开发环境嵌入式知识点复习四 --arm-linux文件编程嵌入式知识点复习五 --arm-linux进程编程嵌入式知识点复习六 --arm-...
  • 嵌入式Linux开发教程(下册)讲述了内核模块与驱动,里面有LED GPIO IIC驱动范例
  • 我们写的程序针对硬件部分抽象出 led_operations 结构体。 上下分层, 比如我们前面写的 LED 驱动程序就分为 2 层: ① 上层实现硬件无关的操作,比如注册字符设备驱动: leddrv.c ② 下层实现硬件相关的操作,比如 ...
  • 嵌入式Linux开发实用教程》

    千次阅读 2019-01-08 08:05:41
    嵌入式Linux开发实用教程》 基本信息 作者: 朱兆祺 李强 袁晋蓉  出版社:人民邮电出版社 ISBN:9787115334831 上架时间:2014-2-13 出版日期:2014 年4月 开本:16开 页码:258 版次:1-1 所属分类...
  • 嵌入式linux驱动开发教程--源代码.rar 嵌入式linux驱动开发教程--源代码.rar
  • LED灯点亮的案例LED灯的原理图有上图可以看出到,开发板上有三盏LED,分别通过LED1、'LED2'和'LED4'四条线连接,从图上可以看出如果对于三盏LED来说,右侧如果为低电平,那么LED将可以被点亮2440连接LED灯的引脚在...
  • 嵌入式linux应用开发完全手册》全面介绍了嵌入式Linux系统开发过程中,从底层系统支持到上层GUI应用的方方面面,内容涵盖Linux操作系统的安装及相关工具的使用、配置,嵌入式编程所需要的基础知识(交叉编译工具的...
  • 搭建开发环境 370 14.1 准备Linux开发主机 371 14.2 安装串口相关软件 375 14.2.1 安装串口驱动 375 14.2.2 安装串口终端软件PuTTY 376 14.2.3 安装串口终端软件minicom 377 14.3 安装TFTP和NFS服务器 379 14.4 准备...
  • 随着智能电子设备的普及,市场对嵌入式开发人员需求增多,嵌入式系统无疑成为了当前最热门极具发展前途的IT应用领域之一。那么嵌入式软件工程师需要学什么?粤嵌将会给大家讲解一下。现在学习嵌入式就业的前景也算是...
  • 嵌入式Linux开发指南》+《开发板使用手册》+《裸机使用手册》 详细手册点击链接下载:https://pan.baidu.com/s/1Xat4C-cDa2Gi1UwNckNRTw 提取码:064r 前言 总领及学习指引 第一部分 开发板入门 第...
  • 一、本文将会以最简单的LED驱动程序带大家走进嵌入式Linux-arm驱动开发,首先,来了解一下最简单的驱动框架。 1.首先struct file_operations led_fops()函数,它是驱动的核心,所有的应用层操作都将通过它调用驱动。...
  • 软件学院大作业设计报告课程名称: 嵌入式Linux应用程序开发题 目: 基于UP-CUP2440平台的驱动程序开发和QT程序开发专 业: 计算机软件班 级: 计算机软件 111 班姓名 学号:鲁 飞8000611038 卢惠民8000611021戚成林...
  • 本帖将全程连载《嵌入式Linux开发教程》这本书的全部内容,对Linux有兴趣的小伙伴,可以在本帖中,挑选喜欢的章节进行学习,有疑问可在相应帖子页面进行跟帖回复,以下便是本社区连载的全部链接: 1、本书简介:...
  • 从本文开始进入我们的重点内容——Linux驱动开发。首先学习字符设备的驱动开发,一些开发流程和驱动代码模板可以作为之后开发的“蓝本”。   字符设备是 Linux 驱动中最基本的一类设备驱动,字符设备就是一个一个...
  • 文章目录Linux 下按键驱动原理程序编写1.修改设备树文件1.1 添加 pinctrl 节点1.2 添加 KEY 设备节点1.3 检查 PIN 是否被其他外设使用2....  按键驱动和 LED 驱动原理上来讲基本都是一样的,都是操作
  • 基于嵌入式LinuxLED驱动开发与应用.pdf
  • 个人深刻觉得像这种嵌入式开发对C语言和微机接口与原理是非常依赖的,必须要有深厚的基础才能hold的住,不然真像一些人说的,学了一年嵌入式感觉还没找到门。 不能再扯了,涉及到linux的驱动开发知识面灰常广,再...
  • 韦东山版嵌入式Linux开发 本书全面介绍了嵌入式 Linux 系统开发过程中,从底层系统支持到上层 GUI 应用的方方面面,内容涵 盖 Linux 操作系统的安装及相关工具的使用、配置,嵌入式编程所需要的基础知识(交叉编译...
  • 本文主要是以一个最简单的LED驱动开发流程,来窥探一下Linux驱动开发为何物。 基本流程: 1.编写驱动文件xxxx.c 这个文件的主要作用是对设备硬件初始化,主要是xxx_init(),其中也包括设备的注册。  对file_...
  • 程序编写   ADC大家非常熟悉... led_switch(LED0,state); } } return 0; } 结果验证   编译下载后,程序正常运行,LCD显示的Ori Value为从ADC寄存器读取的值,Val Value为经过运算后得到的实际采集电压值。

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 7,910
精华内容 3,164
关键字:

led嵌入式linux开发

linux 订阅