嵌入式Linux驱动难?到底难在哪?

驱动入门难在:如何通过自己的学习能力搭建起环境,并理解一个LED驱动。 深入驱动难在:对内核的理解,对特定协议的认识。

新手的想法

最近看到论坛和群里一些人在说驱动难,个别人提出提供的入门资料还是难以入门。

作为嵌入式linux驱动学习的新人,可能心里都有自己的想法,期望有一个自己心中完美的资料来帮助自己入门。

然而,每个人基础不同,悟性不同,对待 问题的态度不同,所以根本难以一个教程满足所有人。

但是总结来说,就是新手可能更希望从现象出发,从最高层出发,从应用出发,然后到底层驱动是如何调用下来的。这个本文可以简单说说。

但本文重点是,驱动难吗,这一行到底难在哪?

本文主题:入门驱动并不难,入门驱动我认为只需要完成LED驱动。如果你理解了LED驱动,我想说你已经入门了,剩下的驱动就是在此基础上进行思考。


资料是加速你学习,为你提供帮助的。

真正在这行成长,一定不要忘记需要的是主动思考和主动学习的能力。

耐得住思考,入门这行没有什么难度。

至于深入,那需要静下来,看书,找资料,自己学习总结理解。

什么是驱动

简单说驱动的作用:就是让设备在操作系统上可以正常运行起来,基于特定协议完成一定功能。

好比都用WINDOS,你来学这一行,我相信你一定给WINWOS装过驱动,比如刚装好PC系统后的网卡驱动,比如串口驱动,就是想让操作系统认可这个设备。

入门驱动需要哪些?

想说真的驱动不难,是很多新手可能会认为我说这句话是自己吹

然而,完成一个简单的LED驱动就真的入门了

为什么:

完成这个驱动,你需要会编译内核

完成这个驱动,你需要会编译驱动

完成这个驱动,你需要会操作外设

完成这个驱动,你需要会让操作系统跑起来。

完成这个驱动,你需要会一些shell指令

完成这个驱动,你可能会了环境搭建,三者互ping。。。。。。

一个LED驱动,也是入门的唯一条件

看看下面这个驱动有哪些?

头文件:写过C程序都知道,肯定需要头文件

特定的格式:既然你写的是linux操作系统的驱动,那肯定需要遵循linux系统的特殊约定,你之前写C没看到过的格式,就是linux下驱动需要遵循的。

LED外设操作:驱动要实现一定功能,对于点灯,需要会看芯片手册,使GPIO输出特定电平。

使用Liuux系统API:一些函数使操作系统提供给开发者的,比如register_chrdev来注册一个字符设备。

以下驱动来自:原1期到2期第一个驱动。

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <asm/uaccess.h>
#include <asm/irq.h>
#include <asm/io.h>
#include <asm/arch/regs-gpio.h>
#include <asm/hardware.h>

static struct class *firstdrv_class;
static struct class_device	*firstdrv_class_dev;

volatile unsigned long *gpfcon = NULL;
volatile unsigned long *gpfdat = NULL;


static int first_drv_open(struct inode *inode, struct file *file)
{
	//printk("first_drv_open\n");
	/* 配置GPF4,5,6为输出 */
	*gpfcon &= ~((0x3<<(4*2)) | (0x3<<(5*2)) | (0x3<<(6*2)));
	*gpfcon |= ((0x1<<(4*2)) | (0x1<<(5*2)) | (0x1<<(6*2)));
	return 0;
}

static ssize_t first_drv_write(struct file *file, const char __user *buf, size_t count, loff_t * ppos)
{
	int val;

	//printk("first_drv_write\n");

	copy_from_user(&val, buf, count); //	copy_to_user();

	if (val == 1)
	{
		// 点灯
		*gpfdat &= ~((1<<4) | (1<<5) | (1<<6));
	}
	else
	{
		// 灭灯
		*gpfdat |= (1<<4) | (1<<5) | (1<<6);
	}
	
	return 0;
}

static struct file_operations first_drv_fops = {
    .owner  =   THIS_MODULE,    /* 这是一个宏,推向编译模块时自动创建的__this_module变量 */
    .open   =   first_drv_open,     
	.write	=	first_drv_write,	   
};


int major;
static int first_drv_init(void)
{
	major = register_chrdev(0, "first_drv", &first_drv_fops); // 注册, 告诉内核

	firstdrv_class = class_create(THIS_MODULE, "firstdrv");

	firstdrv_class_dev = class_device_create(firstdrv_class, NULL, MKDEV(major, 0), NULL, "xyz"); /* /dev/xyz */

	gpfcon = (volatile unsigned long *)ioremap(0x56000050, 16);
	gpfdat = gpfcon + 1;

	return 0;
}

static void first_drv_exit(void)
{
	unregister_chrdev(major, "first_drv"); // 卸载

	class_device_unregister(firstdrv_class_dev);
	class_destroy(firstdrv_class);
	iounmap(gpfcon);
}

module_init(first_drv_init);
module_exit(first_drv_exit);


MODULE_LICENSE("GPL");


如何有全局认识后根据问题学习驱动

大部分人喜欢先看一个情景,然后根据情景,从上到下,认识驱动后,在开始学驱动。

1、研发一个设备,有一个需求,需要通过II2读取外设数据,并显示在设备屏幕上。

2、做应用的:通过read、write来读取数据,并把它显示在界面上。

3、应用去哪里read、write设备数据,对于linux来说一般是设备节点,比如/dev/xxx。这个设备就是驱动要提供给应用的,使应用通过这个设备去获取数据。

4、驱动:哪里来这个设备,自然是使用linux提供的API,向操作系统申请,至于使用什么API,那是内核规定好的,让用啥就用啥。

驱动还要做的就是根据II2协议和外设通信,按照II2协议把数据获取到,这个就和裸机没有太大区别。然后按照linux驱动的框架,把底层数据封装到设备接口,这样应用就可以按照通用的read、write来获取数据了。


入门驱动难在哪

1、难在如何搭建环境

然鹅,这是对于新手最基本的考验,过了这个坎,你才算有能力进入这个行业。不然都像应用那样装一个IDE搞定所有环境,那这行的竞争力还在哪?

2、难在学习能力

如何发挥自己的学习能力,找到解决问题的途径?这就看自己的学习能力和解决问题的能力了,资料都一样,一定是一些人很容易就上手了,有些人始终感觉难,真的思考和去尝试解决了。真的有决心花一天去解决环境问题吗?

深入驱动,才应该是你目前认为难的事

1、比如总线设备驱动模型,platform总线等

2、比如内核协议栈,网络驱动及优化

3、USB协议,USB驱动框架

4、音频、视频,各个子系统

如果还感觉入门驱动难,那么问一下自己

1、自己遇到问题,有主动去查阅各种资料,尝试找到问题解决方法吗

2、有决心花一天,不吃中午饭去搞定环境问题吗

3、自己尝试了多少,真的难吗?



入门驱动只需要会一个led驱动,剩下的驱动就是思考了

在已有的资料上多思考,补充自己还没认知到的,就是很大的进步。


有问题欢迎在下面评论,把你认为难的,不理解的描述出来


attachments-2020-05-u0bmhLq95eafdcdac9947.jpg

3 条评论&回复

请先 登录 后评论
星星之火
星星之火

嵌入式工程师

5 篇文章

作家榜 »

  1. 百问网-周老师 18 文章
  2. st_ashang 14 文章
  3. 渐进 12 文章
  4. zxq 11 文章
  5. helloworld 8 文章
  6. 谢工 5 文章
  7. Litchi_Zheng 5 文章
  8. 星星之火 5 文章