嵌入式驱动开发总结

对于大部分嵌入式产品来说,完整的开发过程基本包含对外部信息的采集(包括不限于传感器,按键,网络端),分析处理后,输出显示到其它设备或控制模拟电路,生成需要的脉冲,电压或电流。在这种背景下,很多模块和接口被提出并被设计出来,如adc, dac, pwm, uart, i2c, spi, qspi, can, eth, sdio, usb, ble, wifi, fsmc, hdmi 等,来应对不同场景的需求。而如何让这些模块正常工作,并能够控制外部的芯片满足最后功能的需求,驱动的概念应用而生,它是应用或系统访问实际硬件的桥梁,如果各种外部硬件是嵌入式系统的五官和手脚,驱动就是其中用于联系它们和大脑的神经,在嵌入式整个产品中当然占有举足轻重的地位。

讲到嵌入式驱动开发,很多时候都会理解为嵌入式linux平台下的驱动开发,这种理解当然有其原因,对于大部分单片机项目,因为需求和设计比较简单,开发中往往并没有驱动的概念,更加不会有嵌入式linux开发中严格分层的概念,但是对寄存器的配置和读写实现对外部硬件的控制,它们事实上也属于驱动的范畴,不过驱动的工作当然远不止如此,根据我在嵌入式产品中的开发经验,驱动开发大致包含如下:

1.对寄存器的配置和修改,完成芯片对于接口的功能设置需求,如模块时钟的开关,gpio的使能,复用功能的配置,中断的使能关断等初始化操作, 然后在通过封装的读写接口,将时序和电平反映外部的引脚上。

2.对外部设备或硬件的访问,通过接口以及设置或者获取芯片引脚的状态,如电平的高低,输入或输出电压的大小,但对于大部分产品来说,我们还需要外部的设备或者硬件来进一步扩展来满足实际功能需求,如epprom, sdram, psram,nandflash/norflash, sdcard, cmos, lcd, keyboard, phy等,这就需要我们在读写的基础上添加对这些外部硬件设备内部状态的读取和功能的配置,以应对目前复杂的需求情况。

从事单片机的开发,这两部分就是整个驱动层需要实现的全部功能,接触过ST的芯片的都知道,无论是最初的标准库还是HAL库,都是通过对底层寄存器的封装,将对模块的具体功能需求的理解和寄存器的操作,转变为对库接口的掌握,或者图形化的界面配置,通过简单的几步,便完成了整个对寄存器的修改和配置,实现功能需求。如果开发实际的项目,这种方法是正确且高效的,不过如果是学习入门阶, 体验下这种便捷也是有利无害,但是如果只通过这种点几下就认为底层配置已经熟悉,从而放弃了继续去深入了解芯片模块和寄存器相关的知识,就本末倒置了,ST Cubemx并不是一项通用的单片机驱动开发技术,不提TI,NXP,新塘等仍然占据了部分市场,HAL本身的性能问题,例如中断中复杂的状态设计和超时处理本身就令人诟病,遇到问题时的维护或者优化,所需要的成本并不低,还是要重新理解掌握,而且工作面试中关注的也往往是对模块的理解(如ADC的精度,串口的流控,波特率等), 这些是图形化的自动生成代码的过程中无法学习掌握的,虽然不同厂商芯片的内部模块布局和寄存器的功能会不一致,但相同功能模块需要配置的信息是基本一致的,不仅对于单片机,对于嵌入式Linux也一样,所以掌握通用技术才是学习的重点,当然真正开发就以方便快捷为主,学习是为了自己,追求的是长期发展,工作是为了利益,追求的是效率,明白这一点十分重要,也不会对图形化有疑惑了。

单片机领域驱动的图形化发展会帮工程师解决了对内部寄存器的访问配置问题,如果只是简单的需求,如输出高低电平,采样电压数据,输出PWM控制波形,串口输出写字符串,对于这些设计来说,图形化是解决了驱动相关的问题,不过现实往往不会这么简单,对外部设备或硬件的访问也是作为嵌入式开发者必须解决的问题,从我刚入门接触的需要单线接口访问的ds18b20, 到后来复杂的PHY,CMOS,USB应用,很多外部模块都需要读取信息来判断状态或者获得数据,写入信息来配置功能,这部分知识其实才是整个驱动中最庞大,复杂且难以掌握的部分,当然这并不是说这部分有多困难,毕竟如果接口封装的合理,这部分也不过是重复读写的操作而已,但是很多时候我们都只是使用通用的,或者对应外设厂商的给予的配好功能的寄存器配置,保证写入正确正常工作就可以,那么难点在哪儿?其实就是需要了解的太多了,而且同一类型的芯片功能可能类似,其内部需要寄存器可能差别很大,像我接触过的摄像头GC0308,OV9281, OV7725,虽然都是使用I2C进行配置,但其中关于寄存器相关的经验很难重复利用,甚至后面的OV系列的寄存器功能都是从设备的厂商获得驱动后,在上面基础上修改的,讲到这,你可能认为这部分没法积累经验,但这是不对的,事实上CMOS调试经验中可以提取的知识有很多, 需图像帧和行的概念,通过时钟对帧率的控制, 图像曝光时间,输出图像的格式(YUV,RGB565, RGB888), 了解图像模糊,图像行错位,图像拼接,颜色偏色等的原因分析和软硬件解决办法,这部分经验如果没有相应行业的经验积累,特别是在学习情况下,是很难总结的,就像非通讯行业很难理解交换芯片的工作原理和机制(即使从事过通讯行业,对于交换芯片的手册的复杂程度仍然记忆犹新), 这种和行业挂钩的知识,我开始也不怎么重视,直到去原厂负责相应模块的功能规划,验证和方案开发的时候,才去深入去了解,这部分经验重要吗?不好说,但这一定是驱动中需要大量时间和经验积累的技术,也是驱动中遇到问题最难解决的部分,这部分经验也很难被量化,但又在驱动开发中占有重要地位。大部分人把这部分理解为移植和修改,当然这是大部分开发的真实情况,这也是我认为比较矛盾的地方。

上面聊了很多,其实并非仅针对单片机相关的硬件操作,对于嵌入式Linux来说,这两部分也是客观存在的,当然对外部设备或硬件的访问在嵌入式Linux开发中仍然是很重要的部分,不过往往这两部分对于嵌入式Linux都有拿来即用的方案(包含原厂和方案商都可能提供), 所以更加被忽视, 特别是对刚入门的人来说,这部分经验用工作来积累是合理的,因为这部分的学习和掌握往往都配合着硬件的调试和分析,需要示波器,逻辑分析仪,电烙铁,各种电气元器件的支持,如果不是大学实验室或者工作后,满足这一套需求,对于大部分人很难承受(当然包含我), 但如果本身也在工作中,因为接手时系统已经稳定,就不在乎这部分的技需要掌握的知识,是不应该的。

不过对于嵌入式Linux驱动开发来说,当然不只和硬件的交互,对于一个完整的分层明显的系统,如何将驱动模块添加到内核中,并满足应用的需求,也是需要掌握的重点之一,这里就提到了嵌入式Linux驱动开发的重点,也是大部分学习中关注的部分,当然也与这部分确定且可以系统的学习有关。

3. 与嵌入式Linux内核的交互接口,对于从单片机转行到嵌入式Linux的开发者,套着单片机的开发经验,往往会比较迷惑,对于LED的操作这类简单的应用,单片机的操作可能只要几行就实现功能,但在嵌入式Linux驱动的开发的第一课中,第一印象就是复杂且迷惑,不过在经历过多个模块的开发应用后,其实这部分并没有如同第二类那么复杂和多变,是有比较系统的思路去理解整个流程的,其实关于嵌入式linux驱动部分也是可以进一步分解成几个方面去进一步学习。

(1)描述驱动模块作者,协议,功能,以及配合lsmod加载,rmmod删除,导出为其它内核模块访问的接口,包括不限于

MODULE_AUTHOR("xxx");                           //模块作者
MODULE_LICENSE("GPL v2");                       //模块许可协议
MODULE_DESCRIPTION("xxxxxxx");                  //模块许描述
MODULE_ALIAS("xxxxxx");                         //模块别名
module_init(func);				//加载的入口函数
module_exit(func);				//删除的释放函数

这部分应该是比较简单的,初次接触应该也能够了解。

(2).将设备添加到总线,并创建设备的相关接口,以及删除时释放相应释放的接口(当然嵌入式Linux也引入了虚拟总线进一步简化了该流程),具体如下

//注册或申请设备号
register_chrdev_region(...)
alloc_chrdev_region(...)     
//将设备接口,实际访问的硬件(open, read, write,close)等接口ops,与设备号关联
cdev_init(...)
cdev_add(...)
//创建设备对象,用于上层应用的通过/dev/*来访问
class_create(...)
device_create(...)
//释放驱动相关的资源
device_destroy(...)
class_destroy(...)
cdev_del(...)
unregister_chrdev_region(...)

在这个基础上,又引入了虚拟总线的概念,使用

platform_device_unregister(...)
platform_device_register(...)

可以更方便的开发和总结完成对于驱动的开发。

当然,比单片机开发来说,嵌入式Linux需要更多的考虑同步/异步,多核访问的问题,对于中断和事件的处理也会更加复杂,不过这部分并没有困难到无法掌握,在工作中遇到问题,解决问题,在充分的总结即可,不过如果看过我之前关于如何自学嵌入式Linux的文章

怎样自学嵌入式LINUX?​www.zhihu.com图标

就会发现我遗漏了关于设备树相关的说明,从逻辑上讲,DTS是对芯片硬件接口的抽象化实例,是对内部寄存器的封装转换,来解决Linux内核中不同板级的驱动代码的冗余问题,而在驱动开发中,它也需要通过内核接口的访问,配合完整封装的INPUT子系统,SPI框架,I2C框架,USB框架等,来快速应用完成对外部硬件设备的操作。放在上面任意一类讲并不合适,它是Linux后期为解决实际问题引入的新的机制,但目前已经被ARM系大部分厂商所接受,而且逐渐成为主流,所以理解设备树机制,并将其与芯片内部硬件的配置关联,从而实现满足需求的应用方案在驱动开发中已经成为主流,也是嵌入式Linux驱动开发必须掌握的知识之一。

看到这里,应该可以把嵌入式驱动开发需要知识归类于以下方面知识掌握:

1.对芯片本身模块,寄存器和内部总线信息的学习了解

2.对外部多样功能芯片的熟悉,特别是对自己所在行业需要了解的芯片,如图像行业的CMOS,通讯行业的交换芯片,PHY,物联网行业BLE模块,WIFI模块,2G/3G/4G/nb-IOT模块,工控行业的各种传感器的采样, 电机控制等知识

了解这些,对于单片机领域的驱动方面已经完全足够了,不过对于嵌入式Linux驱动来说,在这基础上

3.理解和驱动相关的内核接口,总线执行机制,上层应用的访问接口,在实现应用的接口上,同时能够掌握DTS语法,能够将设备树上的信息与芯片本身硬件的知识在脑海中对应联系,完成修改和添加的功能,并能够将其正确的用于驱动中。

讲述了这些,大致嵌入式驱动的整体有了清晰的认知,这不是学习的方法路径,而且从实践的角度说明驱动关注的是什么,什么是需要去了解和掌握的,这部分也是我这几年工作积累的收获之一,嵌入式驱动开发是值得钻研并掌握的一门技术,学习的深入了,也会理解很多Linux内核设计的机制,体验最强大开源项目的魅力,但对于入门我还是不建议去一个一个外设模块去按部就班的去了解,上来要先建立整体的概念,从产品或者需求去拆解,不仅学到的是怎么做,还会学到为什么这么做,以及怎么才能做的更好。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值