驱动入门难在:如何通过自己的学习能力搭建起环境,并理解一个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驱动,剩下的驱动就是思考了
在已有的资料上多思考,补充自己还没认知到的,就是很大的进步。
有问题欢迎在下面评论,把你认为难的,不理解的描述出来。
没有思考就没有发言权,总结一下驱动到底怎么学:
1.弄懂硬件工作原理。入门驱动只需要会一个led驱动,实际上就是一个高低电平(弄懂硬件工作原理),正规说法懂硬件时序,会看波形分析协议(单片机层次)
2.懂基于某操作系统的框架,连接总线(linux各种总线协议)。
3.懂产品需求分析。用什么硬件,走什么驱动(设计一个模块,在某平台下正常work)。